Deserialization

Concerning deserialization, almost the same applies as to serialization. Each message generated has a deserialize() function, which takes an object derived from ReadBufferInterface as input. The actual implementation which you use is the ReadBufferFixedSize template class. Specify the number of bytes allocated in the class via the template parameter. Push the bytes you received into it, and hand it to the message:

::EmbeddedProto::ReadBufferFixedSize<64> buffer;

// Push the bytes received over your communication line.
buffer.push(byte);

auto deserialize_status = msg.deserialize(buffer);
if(::EmbeddedProto::Error::NO_ERRORS == deserialize_status)
{
  // The message is ready to use.
}

Before version 3.2.0, the user of this library had to write an implementation for the ReadBufferInterface. The class should hold the serialized message received over your specific communications method. For instance, it could contain all the packages received over a TCP connection. If you need more documentation on the ReadBufferInterface class, you can find it here.

The interface also has a pop(const bytes_view& dest) function for a block of bytes. It has a default implementation which loops over the single byte pop(). As of version 4.0.0 the library reads a repeated field of fixed-width numbers as one block, so override it with a block copy in your own buffer class. See packed fixed-width fields.

After an error

When deserialize() returns an error, for instance END_OF_BUFFER because the message was not complete, the message remembers the field it was working on. A second call with more data continues with that field instead of reading a new tag. When you would rather start over, for instance with the next message, call clear() on the message first. This also resets all fields to their default values.

Deserializing in parts

As with serialization, a message can be deserialized from a buffer smaller than the message. Define PARTIAL_SERIALIZATION_ENABLED when building. Each message then has a function deserialize_partial(), which takes the buffer and a state object. Call it each time a part arrives. It returns END_OF_BUFFER when it has consumed the buffer and expects more:

::EmbeddedProto::ReadBufferFixedSize<32> buffer;
Foo::StateStack state;

// Each time a part has arrived and has been pushed into the buffer:
auto status = msg.deserialize_partial(buffer, state.root());
if(::EmbeddedProto::Error::END_OF_BUFFER == status)
{
  // Wait for the next part.
  buffer.clear();
}

Please note that a message on the wire does not say where it ends. The message is complete when the last part has been consumed and the state is back at the start of a field, that is when state.root().phase equals FieldProcessingPhase::TAG. Knowing where the last part ends is up to your communication protocol, for instance by sending the total length in front of the message, as the UART example does.

An END_OF_BUFFER in any other phase means the buffer ended in the middle of a field, and more data is needed. The function only returns NO_ERRORS for a delimited message, which does say where it ends: when its end marker has been consumed.