Home

Awesome

Prototypes only -- please do not yet expect a fully working project. A protocol finalization has yet to be made. Feedback is greatly appreciated.


This project is a leightweight binary serialization protocol and provides implementations for efficient decoding and encoding multiple payloads pairs into a single byte stream (package). The main goals are flexibility, high encoding and decoding performance and minimal byte and implementation overhead.

Features

Big-endian ordering is used throughout the protocol for all bits in a byte, and all bytes in byte-groups (i.e. the most significant comes first, followed by less significant).

The serialization works similar to [Protocol Buffers] 1, using a variable amount of length-bytes (base-128 varints) to indicate the number of bytes to read for the next payload.

Example

Let's build a simple example request:

encoder = BinaryEncoder()
encoder.setType(13)
encoder.put(0, "hello")
encoder.put(1, 123)
# payload 2 not set, 0x00 will be inserted by default
encoder.put(3, "world")

result = encoder.encode()

Encoded Request

00 0D | 05 h e l l o | 01 123 | 01 00 | 05 w o r l d

Resulting package

0x00   0x0D   0x05   hello  0x01   123    0x10    0x00   0x05   world
|      |      |      |      |      |      |       |      |      |
|      |      |      |      |      |      |       |      |      +- last payload: "world"
|      |      |      |      |      |      |       |      +-------- length of last payload: 5
|      |      |      |      |      |      |       |
|      |      |      |      |      |      |       +- third payload: 0x00
|      |      |      |      |      |      +--------- length of third payload: 1 byte
|      |      |      |      |      |      
|      |      |      |      |      +- second payload: 123
|      |      |      |      +-------- length of second payload: 1 byte
|      |      |      |       
|      |      |      +- first payload: "hello"
|      |      +-------- length of first payload: 5 bytes
|      |    
|      +- package type: 13 = 0x0D
+-------- package start byte

Pseude code for the serialization process

result = bytearray()
result.insert(0, 0x00) # request start byte  
result.insert(packageType)

for payload in payloadsToEncode:
    result.extend(payload.length_varint_bytes)
    result.extend(payload.content_bytes)

Length Bytes

Length bytes are base-128 varints prepending every payload, representing the payload's size in bytes.

Base 128 Varints

Paraphrasing the [protocol buffers documentation] 1:

To understand the encoding, you first need to understand varints. Varints are a method of serializing integers using one or more bytes. Smaller numbers take a smaller number of bytes.

Each byte in a varint, except the last byte, has the most significant bit (msb) set – this indicates that there are further bytes to come. The lower 7 bits of each byte are used to store the representation of the number in groups of 7 bits.

This project differs with the protobuf base-128 varint implementation: Protocol Buffers puts the least significant bytes first, followed by more significant bytes. This project keeps the natural byte order with the most significant bytes first, followed by the less significant bytes.

Byte Overview

Bit Values: [ x | 64 | 32 | 16 | 8 | 4 | 2 | 1 ]
              | 
              + last-varint-byte indicator (0=last, 1=next-also-length-byte)

Limits Since each varint-byte uses only 7 bits for representing the number, the following limits arise:

One varint-byte:  7 bits   = 2^7  = 0..127
Two varint-bytes: 2*7 bits = 2^14 = 0..16383  

A number greater than 16383 generates a third varint-byte, etc.

Varint Examples

number  |  [  varint byte 1  ] | [  varint byte 0  ]  
--------+----------------------+---------------------  
    1   |                      | [ 0 0 0 0 0 0 0 1 ]
  127   |                      | [ 0 1 1 1 1 1 1 1 ]
  128   |  [ 1 0 0 0 0 0 0 1 ] | [ 0 0 0 0 0 0 0 0 ]  
  255   |  [ 1 0 0 0 0 0 0 1 ] | [ 0 1 1 1 1 1 1 1 ]
  256   |  [ 1 0 0 0 0 0 1 0 ] | [ 0 0 0 0 0 0 0 0 ]
16383   |  [ 1 1 1 1 1 1 1 1 ] | [ 0 1 1 1 1 1 1 1 ]