Package: pbtools
AboutGoogle Protocol Bufferstools in Python 3.6+.Csource code generator.proto3language parser.Known limitations:Options, services (gRPC) and reserved fields are ignored.Public imports are not implemented.Project homepage:https://github.com/eerimoq/pbtoolsDocumentation:https://pbtools.readthedocs.ioInstallationpipinstallpbtoolsC source code designThe C source code is designed with the following in mind:Clean and easy to use API.No malloc/free. Uses a workspace/arena for memory allocations.Fast encoding and decoding.Small memory footprint.Thread safety.Known limitations:charmust be 8 bits.ToDo:Makemapeasier to use. Only one allocation should be needed
before encoding, not one per sub-message item.Memory managementA workspace, or arena, is used to allocate memory when encoding and
decoding messages. For simplicity, allocated memory can’t be freed,
which puts restrictions on how a message can be modified between
encodings (if one want to do that). Scalar value type fields (ints,
strings, bytes, etc.) can be modified, but the length of repeated
fields can’t.Scalar Value TypesProtobuf scalar value types are mapped to C types as shown in the
table below.Protubuf TypeC Typedoubledoublefloatfloatint32int32_tint64int64_tuint32uint32_tuint64uint64_tsint32int32_tsint64int64_tfixed32int32_tfixed64int64_tsfixed32int32_tsfixed64int64_tboolboolstringchar *bytesstruct { uint8_t *buf_p, size_t size }MessageA message is a struct in C.For example, let’s create a protocol specification.syntax="proto3";packagefoo;messageBar{boolv1=1;}messageFie{int32v2=1;Barv3=2;}One struct is generated per message.structfoo_bar_t{boolv1;};structfoo_fie_t{int32_tv2;structfoo_bar_t*v3_p;};The sub-messagev3has to be allocated before encoding and checked
ifNULLafter decoding.structfoo_fie_t*fie_p;/* Encode. */fie_p=foo_fie_new(...);fie_p->v2=5;foo_fie_v3_alloc(fie_p);fie_p->v3_p->v1=true;foo_fie_encode(fie_p,...);/* Decode. */fie_p=foo_fie_new(...);foo_fie_decode(fie_p,...);printf("%d\n",fie_p->v2);if(fie_p->v3_p!=NULL){printf("%d\n",fie_p->v3_p->v1);}OneofA oneof is an enum (the choice) and a union in C.For example, let’s create a protocol specification.syntax="proto3";packagefoo;messageBar{oneoffie{int32v1=1;boolv2=2;};}One enum and one struct is generated per oneof.enumfoo_bar_fie_e{foo_bar_fie_none_e=0,foo_bar_fie_v1_e=1,foo_bar_fie_v2_e=2};structfoo_bar_t{enumfoo_bar_fie_choice_efie;union{int32_tv1;boolv2;};};The generated code can encode and decode messages. Call_<field>_init()or_<field>_alloc()to select which oneof
field to encode. Use theenumto check which oneof field was
decoded (if any).structfoo_bar_t*bar_p;/* Encode with choice v1. */bar_p=foo_bar_new(...);foo_bar_v1_init(bar_p);bar_p->v1=-2;foo_bar_encode(bar_p,...);/* Decode. */bar_p=foo_bar_new(...);foo_bar_decode(bar_p,...);switch(bar_p->fie){casefoo_bar_fie_none_e:printf("Not present.\n");break;casefoo_bar_fie_v1_e:printf("%d\n",bar_p->v1);break;casefoo_bar_fie_v2_e:printf("%d\n",bar_p->v2);break;default:printf("Can not happen.\n");break;}BenchmarkSeebenchmarkfor a benchmark of a few C/C++ protobuf libraries.Example usageC source codeIn this example we use the simple proto-filehello_world.proto.syntax="proto3";packagehello_world;messageFoo{int32bar=1;}Generate C source code from the proto-file.$ pbtools generate_c_source examples/hello_world/hello_world.protoSeehello_world.handhello_world.cfor the contents of the
generated files.We’ll use the generated types and functions below.structhello_world_foo_t{structpbtools_message_base_tbase;int32_tbar;};structhello_world_foo_t*hello_world_foo_new(void*workspace_p,size_tsize);inthello_world_foo_encode(structhello_world_foo_t*self_p,void*encoded_p,size_tsize);inthello_world_foo_decode(structhello_world_foo_t*self_p,constuint8_t*encoded_p,size_tsize);Encode and decode the Foo-message inmain.c.#include<stdio.h>#include"hello_world.h"intmain(intargc,constchar*argv[]){intsize;uint8_tworkspace[64];uint8_tencoded[16];structhello_world_foo_t*foo_p;/* Encode. */foo_p=hello_world_foo_new(&workspace[0],sizeof(workspace));if(foo_p==NULL){return(1);}foo_p->bar=78;size=hello_world_foo_encode(foo_p,&encoded[0],sizeof(encoded));if(size<0){return(2);}printf("Successfully encoded Foo into %d bytes.\n",size);/* Decode. */foo_p=hello_world_foo_new(&workspace[0],sizeof(workspace));if(foo_p==NULL){return(3);}size=hello_world_foo_decode(foo_p,&encoded[0],size);if(size<0){return(4);}printf("Successfully decoded %d bytes into Foo.\n",size);printf("Foo.bar: %d\n",foo_p->bar);return(0);}Build and run the program.$ gcc -I lib/include main.c hello_world.c lib/src/pbtools.c -o main
$ ./main
Successfully encoded Foo into 2 bytes.
Successfully decoded 2 bytes into Foo.
Foo.bar: 78Seeexamples/hello_worldfor all files used in this example.Command line toolThe generate C source subcommandBelow is an example of how to generate C source code from a
proto-file.$ pbtools generate_c_source examples/address_book/address_book.protoSeeaddress_book.handaddress_book.cfor the contents of the
generated files.