hit9 / bitproto

The bit level data interchange format for serializing data structures (long term maintenance).
https://bitproto.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
127 stars 16 forks source link
bitproto data-exchange data-interchange embedded marshalling protocol serialization serialization-library

The bit level data interchange format

Introduction

Bitproto is a fast, lightweight and easy-to-use bit level data interchange format for serializing data structures.

The protocol describing syntax looks like the great protocol buffers, but in bit level:

proto example

message Data {
    uint3 the = 1
    uint3 bit = 2
    uint5 level = 3
    uint4 data = 4
    uint11 interchange = 6
    uint6 format = 7
}  // 32 bits => 4B

The Data above is called a message, it consists of 7 fields and will occupy a total of 4 bytes after encoding.

This image shows the layout of data fields in the encoded bytes buffer:

Code Example

Code example to encode bitproto message in C:

struct Data data = {.the = 7,
                    .bit = 7,
                    .level = 31,
                    .data = 15,
                    .interchange = 2047,
                    .format = 63};
unsigned char s[BYTES_LENGTH_DATA] = {0};
EncodeData(&data, s);
// length of s is 4, and the hex format is
// 0xFF 0xFF 0xFF 0xFF

And the decoding example:

struct Data d = {0};
DecodeData(&d, s);
// values of d's fields is now:
// 7 7 31 15 2047 63

Simple and green, isn't it?

Code patterns of bitproto encoding are exactly similar in C, Go and Python.

Features

Schema Example

An example for a simple overview of the bitproto schema grammar:

proto pen

// Constant value
const PEN_ARRAY_SIZE = 2 * 3;

// Bit level enum.
enum Color : uint3 {
    COLOR_UNKNOWN = 0
    COLOR_RED = 1
    COLOR_BLUE = 2
    COLOR_GREEN = 3
}

// Type alias
type Timestamp = int64

// Composite structure
message Pen {
    Color color = 1
    Timestamp produced_at = 2
    uint3 number = 3
    uint13 value = 4
}

message Box {
    // Fixed-size array
    Pen[PEN_ARRAY_SIZE] pens = 1;
}

Run the bitproto compiler to generate C files:

$ bitproto c pen.bitproto

Which generates two files: pen_bp.h and pen_bp.c.

We can have an overview of the generated code for the C language:

// Constant value
#define PEN_ARRAY_SIZE 6

// Bit level enum.
typedef uint8_t Color; // 3bit

#define COLOR_UNKNOWN 0
#define COLOR_RED 1
#define COLOR_BLUE 2
#define COLOR_GREEN 3

// Type alias
typedef int64_t Timestamp; // 64bit

// Number of bytes to encode struct Pen
#define BYTES_LENGTH_PEN 11

// Composite structure
struct Pen {
    Color color; // 3bit
    Timestamp produced_at; // 64bit
    uint8_t number; // 3bit
    uint16_t value; // 13bit
};

// Number of bytes to encode struct Box
#define BYTES_LENGTH_BOX 63

struct Box {
    // Fixed-size array
    struct Pen pens[6]; // 498bit
};

You can checkout directory example for a larger example.

Why bitproto ?

There is protobuf, why bitproto?

Origin

The bitproto was originally made when I'm working with embedded programs on micro-controllers. Where usually exists many programming constraints:

Protobuf does not live on embedded field natively, it doesn't target ANSI C out of box.

Scenario

It's recommended to use bitproto over protobuf when:

For scenarios other than the above, I recommend to use protobuf over bitproto.

Vs Protobuf

The differences between bitproto and protobuf are:

Known Shortcomes

Documentation and Links

Documentation:

Editor syntax highlighting plugins:

Faq:

Blog posts:

License

BSD3