JSON BinPack

repository·main·Indexed 41 days ago

https://github.com/sourcemeta/jsonbinpack

A space-efficient binary serialization format for JSON documents supporting both schema-driven (using JSON Schema 2020-12) and schema-less modes. Designed for high compression in low-bandwidth environments, it provides C++ classes such as Encoder, Decoder, InputStream, and OutputStream for serializing and deserializing data.

Tokens
2.9K
Snippets
9
Records
14
Agent score
50%

What's inside JSON BinPack

  1. Overview of JSON BinPack

    main

    JSON BinPack is an open-source binary JSON serialization format designed for high space efficiency. It is a hybrid format that supports two modes of operation:

    1. Schema-driven mode: Uses a JSON Schema 2020-12 definition to encode documents, leveraging semantic information to achieve high compression.
    2. Schema-less mode: Allows for encoding any JSON document without a strict schema, providing flexibility for free-form data.

    Key benefits include:

    • Space Efficiency: Often outperforms general-purpose compressors like GZIP, LZMA, and LZ4, especially for small to medium-sized documents.
    • Standardized Schemas: Uses industry-standard JSON Schema, making it compatible with existing OpenAPI, AsyncAPI, or RAML specifications.
    • Hybrid Flexibility: Allows you to be as strict or as loose as needed within the same document or across different use cases.
  2. JSON BinPack schema evolution

    main
    Because JSON BinPack adopts the industry-standard JSON Schema, it is compatible with the wider JSON Schema ecosystem's approach to schema evolution. You can use existing tooling and methodologies, such as Cambria, to manage changes to your data structures over time.
  3. When to use JSON BinPack vs other serialization formats

    main

    Choosing the right format depends on your primary bottleneck (bandwidth vs. CPU/latency):

    Use JSON BinPack when:

    • Your primary goal is space efficiency (reducing payload size).
    • You are transmitting data over unreliable or low-bandwidth connections (e.g., Mobile or IoT systems).
    • You want to leverage existing JSON Schema definitions.
    • You can trade more CPU cycles for better compression to improve network performance.

    Consider alternatives (like FlatBuffers or Cap'n Proto) when:

    • You require extreme runtime-efficiency.
    • You need zero-copy serialization.
    • You are performing intra-service or inter-process communication (IPC) where computation/latency is the bottleneck rather than network bandwidth.
  4. How JSON BinPack handles unknown fields

    main

    JSON BinPack relies on the expressive constraint-based nature of JSON Schema.

    • Encoding: You can encode free-form data and unknown fields as long as your JSON Schema definition is written to permit them (e.g., using additionalProperties).
    • Deserialization: Deserializing data that does not match the provided JSON Schema definition is considered undefined behavior and will likely result in an exception being thrown.
  5. Serialize JSON data using the Encoder class

    main

    The Encoder class is used to serialize sourcemeta::core::JSON documents into the JSON BinPack binary format. You initialize the Encoder with a Stream object and then call the write method, specifying the desired Encoding type.

    To use the Encoder, you must provide a sourcemeta::core::JSON document and an Encoding constant that defines how the data should be packed.

    #include <sourcemeta/jsonbinpack/runtime_encoder.h>
    
    // Assuming 'stream' is a valid implementation of a Stream
    sourcemeta::jsonbinpack::Encoder encoder(stream);
    
    // 'document' is a sourcemeta::core::JSON object
    // 'encoding' is a valid Encoding type from the specification
    encoder.write(document, encoding);
  6. Use InputStream for decoding JSON BinPack data

    main

    The sourcemeta::jsonbinpack::InputStream class provides a specialized interface for reading BinPack-encoded data from a standard C++ input stream. It inherits from sourcemeta::core::BinaryReader and provides methods to consume common BinPack primitives like varints and UTF-8 strings.

    To use it, instantiate an InputStream by passing a reference to a std::basic_istream configured with sourcemeta::core::JSON::Char and sourcemeta::core::JSON::CharTraits.

    #include <sourcemeta/jsonbinpack/runtime_input_stream.h>
    #include <fstream>
    
    // Assuming a stream configured with sourcemeta::core::JSON traits
    sourcemeta::jsonbinpack::InputStream input(my_istream);
    
    // Example usage of primitives
    std::uint64_t val = input.get_varint();
    std::int64_t zigzag_val = input.get_varint_zigzag();
    auto str = input.get_string_utf8(length);
  7. Use OutputStream for JSON BinPack encoding

    main

    The OutputStream class provides a specialized interface for writing JSON BinPack encoded data to a standard C++ output stream. It inherits from sourcemeta::core::BinaryWriter and is designed to work with std::basic_ostream using sourcemeta::core::JSON::Char and sourcemeta::core::JSON::CharTraits.

    Use this class when you need to manually drive the encoding process into an existing stream using low-level primitives like varints and UTF-8 strings.

    #include <sourcemeta/jsonbinpack/runtime_output_stream.h>
    #include <iostream>
    
    // Assuming a stream is already available
    std::ostream my_stream;
    // Initialize the OutputStream with the target stream
    sourcemeta::jsonbinpack::OutputStream output_stream(my_stream);
    
    // Use primitives to write data
    output_stream.put_varint(42);
    output_stream.put_string_utf8("example", 7);
  8. Decode JSON BinPack data with the Decoder class

    main

    The Decoder class is used to parse JSON BinPack binary data into sourcemeta::core::JSON objects. To use it, instantiate a Decoder with a Stream object representing your input source. You can then use the read method, passing an Encoding object to specify how the next chunk of data should be interpreted according to the JSON BinPack specification.

    #include <sourcemeta/jsonbinpack/runtime_decoder.h>
    
    // Assuming 'input_stream' is a valid sourcemeta::jsonbinpack::Stream
    sourcemeta::jsonbinpack::Decoder decoder(input_stream);
    
    // Read data using a specific encoding
    auto json_data = decoder.read(some_encoding);
  9. Reference the JSON BinPack encoding types

    main

    The Encoder supports various encoding strategies defined by the JSON BinPack specification. These encodings are categorized by the data type they handle: Integers, Numbers, Any, Strings, Arrays, and Objects.

    Note that encoding names follow the JSON BinPack specification casing rather than standard C++ naming conventions.

    // Integer encodings
    BOUNDED_MULTIPLE_8BITS_ENUM_FIXED
    FLOOR_MULTIPLE_ENUM_VARINT
    ROOF_MULTIPLE_MIRROR_ENUM_VARINT
    ARBITRARY_MULTIPLE_ZIGZAG_VARINT
    
    // Number encodings
    DOUBLE_VARINT_TUPLE
    
    // 'Any' encodings
    BYTE_CHOICE_INDEX
    LARGE_CHOICE_INDEX
    TOP_LEVEL_BYTE_CHOICE_INDEX
    CONST_NONE
    ANY_PACKED_TYPE_TAG_BYTE_PREFIX
    
    // String encodings
    UTF8_STRING_NO_LENGTH
    FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED
    ROOF_VARINT_PREFIX_UTF8_STRING_SHARED
    BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED
    RFC3339_DATE_INTEGER_TRIPLET
    PREFIX_VARINT_LENGTH_STRING_SHARED
    
    // Array encodings
    FIXED_TYPED_ARRAY
    BOUNDED_8BITS_TYPED_ARRAY
    FLOOR_TYPED_ARRAY
    ROOF_TYPED_ARRAY
    
    // Object encodings
    FIXED_TYPED_ARBITRARY_OBJECT
    VARINT_TYPED_ARBITRARY_OBJECT
  10. OutputStream encoding primitives

    main

    The OutputStream class provides the following methods for writing encoded data:

    • put_varint(const std::uint64_t value): Writes an unsigned 64-bit integer using variable-length encoding.
    • put_varint_zigzag(const std::int64_t value): Writes a signed 64-bit integer using ZigZag encoding followed by variable-length encoding.
    • put_string_utf8(const sourcemeta::core::JSON::String &string, const std::uint64_t length): Writes a UTF-8 encoded string of the specified length.
    auto put_varint(const std::uint64_t value) -> void;
    auto put_varint_zigzag(const std::int64_t value) -> void;
    auto put_string_utf8(const sourcemeta::core::JSON::String &string, const std::uint64_t length) -> void;