Define uint8_t variable in Protocol Buffers message file

Viewed 5386

I want to define a Point message in Protocol Buffers which represents an RGB Colored Point in 3-dimensional space.

message Point {
    float x   = 1;
    float y   = 2;
    float z   = 3;
    uint8_t r = 4;
    uint8_t g = 5;
    uint8_t b = 6;
}

Here, x, y, z variables defines the position of Point and r, g, b defines the color in RGB space.

Since uint8_t is not defined in Protocol Buffers, I am looking for a workaround to define it. At present, I am using uint32 in place of uint8_t.

3 Answers

There isn't anything in protobuf that represents a single byte - it simply isn't a thing that the wire-format worries about. The options are:

  • varint (up to 64 bits input, up to 10 bytes on the wire depending on the highest set bit)
  • fixed 32 bit
  • fixed 64 bit
  • length-prefixed (strings, sub-objects, packed arrays)
  • (group tokens; a rare implementation detail)

A single byte isn't a good fit for any of those. Frankly, I'd use a single fixed32 for all 3, and combine/decompose the 3 bytes manually (via shifting etc). The advantage here is that it would only have one field header for the 3 bytes, and wouldn't be artificially stretched via having high bits (I'm not sure that a composed RGB value is a good candidate for varint). You'd also have a spare byte if you want to add something else at a later date (alpha, maybe).

So:

message Point {
    float x     = 1;
    float y     = 2;
    float z     = 3;
    fixed32 rgb = 4;
}

IMHO this is the correct approach. You should use the nearest data type capable of holding all values to be sent between the system. The source & destination systems should validate the data if it is in the correct range. For uint8_t this is int32 indeed.

Some protocol buffers implementations actually allow this. In particular, nanopb allows to either have .options file alongside the .proto file or use its extension directly in .proto file to fine tune interpretation of individual fields.

Specifying int_size = IS_8 will convert uint32 from message to uint8_t in generated structure.

import "nanopb.proto";

message Point {
    float x  = 1;
    float y  = 2;
    float z  = 3;
    uint32 r = 4 [(nanopb).int_size = IS_8];
    uint32 g = 5 [(nanopb).int_size = IS_8];
    uint32 b = 6 [(nanopb).int_size = IS_8];
}
Related