libsignal-protocol-c  master
Generated Code

For each enum, we generate a C enum. For each message, we generate a C structure which can be cast to a ProtobufCMessage.

For each enum and message, we generate a descriptor object that allows us to implement a kind of reflection on the structures.

First, some naming conventions:

  • The name of the type for enums and messages and services is camel case (meaning WordsAreCrammedTogether) except that double underscores are used to delimit scopes. For example, the following .proto file:
package foo.bar;
message BazBah {
optional int32 val = 1;
}

would generate a C type Foo__Bar__BazBah.

  • Identifiers for functions and globals are all lowercase, with camel case words separated by single underscores. For example, one of the function prototypes generated by protoc-c for the above example:
Foo__Bar__BazBah *
foo__bar__baz_bah__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
  • Identifiers for enum values contain an uppercase prefix which embeds the package name and the enum type name.
  • A double underscore is used to separate further components of identifier names.

For example, in the name of the unpack function above, the package name foo.bar has become foo__bar, the message name BazBah has become baz_bah, and the method name is unpack. These are all joined with double underscores to form the C identifier foo__bar__baz_bah__unpack.

We also generate descriptor objects for messages and enums. These are declared in the .pb-c.h files:

extern const ProtobufCMessageDescriptor foo__bar__baz_bah__descriptor;

The message structures all begin with ProtobufCMessageDescriptor * which is sufficient to allow them to be cast to ProtobufCMessage.

For each message defined in a .proto file, we generate a number of functions and macros. Each function name contains a prefix based on the package name and message name in order to make it a unique C identifier.

  • INIT. Statically initializes a message object, initializing its descriptor and setting its fields to default values. Uninitialized messages cannot be processed by the protobuf-c library.
#define FOO__BAR__BAZ_BAH__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&foo__bar__baz_bah__descriptor), 0 }
  • init(). Initializes a message object, initializing its descriptor and setting its fields to default values. Uninitialized messages cannot be processed by the protobuf-c library.
void foo__bar__baz_bah__init
(Foo__Bar__BazBah *message);
  • unpack(). Unpacks data for a particular message format. Note that the allocator parameter is usually NULL to indicate that the system's malloc() and free() functions should be used for dynamically allocating memory.
Foo__Bar__BazBah *
foo__bar__baz_bah__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
  • free_unpacked(). Frees a message object obtained with the unpack() method.
void foo__bar__baz_bah__free_unpacked
(Foo__Bar__BazBah *message,
ProtobufCAllocator *allocator);
  • get_packed_size(). Calculates the length in bytes of the serialized representation of the message object.
size_t foo__bar__baz_bah__get_packed_size
(const Foo__Bar__BazBah *message);
  • pack(). Pack a message object into a preallocated buffer. Assumes that the buffer is large enough. (Use get_packed_size() first.)
size_t foo__bar__baz_bah__pack
(const Foo__Bar__BazBah *message,
uint8_t *out);
  • pack_to_buffer(). Packs a message into a "virtual buffer". This is an object which defines an "append bytes" callback to consume data as it is serialized.
size_t foo__bar__baz_bah__pack_to_buffer
(const Foo__Bar__BazBah *message,
ProtobufCBuffer *buffer);