protobuf-c extend workaround
protobuf-c unfortunately doesn't support the extend directive, such as:
message extendingmsg {
required string name = 1;
extend msg {
optional extendingmsg msg_ext = 1000;
};
};
But there is a workaround, namely the base.unknown_fields member of the base message. The unknown field for a message is a Length-delimited field, so it starts off with the payload length encoded in varint encoding followed by the payload itself - the extending message.
Thus, you do this:
- unpack the protobuf as the base message type ('msg' in the example above)
- the extending message ('extendingmsg' above) will (most likely) be the first&only unknown field.
- decode the varint from msg->base.unknown_fields[0], make sure to keep track of the size of the varint itself (hence the var-part of varint)
- now skip past the varint and decode the remaining payload as your extending message type ('extendingmsg' above).
Or in code:
int handle_protobuf_extendingmsg (unsigned char *buf, int len) {
msg *m;
extendingmsg *em;
int vlen;
uint64_t plen;
/* Unpack containign msg message. */
if (!(m = msg__unpack(NULL, len, buf)))
return -1;
if (m->base.n_unknown_fields < 1) {
msg__free_unpacked(m, NULL);
return -1;
}
/* The message starts past the varint-encoded length. */
plen = rd_varint_decode_u64(
m->base.unknown_fields[0].data,
m->base.unknown_fields[0].len, &vlen);
if (vlen <= 0) {
/* varint decode failed */
msg__free_unpacked(m, NULL);
return -1;
}
/* Unpack the extendingmsg past the varint */
if (!(em =
extendingmsg__unpack(NULL,
m->base.unknown_fields[0].len-vlen,
m->base.unknown_fields[0].data+vlen))) {
msg__free_unpacked(m, NULL);
return -1;
}
/* em and e are now unpacked and ready to use
* as two separeate protobuf messages. */
printf("name: %s\n", em->name);
extendingmsg__free_unpacked(em, NULL);
msg__free_unpacked(m, NULL);
return 0;
}
The above code utilizes librd's varint decoder which can be found here:
https://github.com/edenhill/librd