Reorganize the field decoder interface.

This makes the field decoding functions more intuitive to use.
The old interface is still present if you specify NANOPB_INTERNALS.

Update issue 2
Status: FixedInGit
This commit is contained in:
Petteri Aimonen
2012-08-24 20:23:25 +03:00
parent dc2da0edc5
commit 2941e90e04
6 changed files with 150 additions and 152 deletions

View File

@@ -320,16 +320,6 @@ In addition to EOF, the pb_decode implementation supports terminating a message
For optional fields, this function applies the default value and sets *has_<field>* to false if the field is not present. For optional fields, this function applies the default value and sets *has_<field>* to false if the field is not present.
pb_decode_varint
----------------
Read and decode a varint_ encoded integer. ::
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
:stream: Input stream to read from. 1-10 bytes will be read.
:dest: Storage for the decoded integer. Value is undefined on error.
:returns: True on success, false if value exceeds uint64_t range or an IO error happens.
pb_skip_varint pb_skip_varint
-------------- --------------
Skip a varint_ encoded integer without decoding it. :: Skip a varint_ encoded integer without decoding it. ::
@@ -373,48 +363,41 @@ Remove the data for a field from the stream, without actually decoding it::
:wire_type: Type of field to skip. :wire_type: Type of field to skip.
:returns: True on success, false on IO error. :returns: True on success, false on IO error.
.. sidebar:: Field decoders .. sidebar:: Decoding fields manually
The functions with names beginning with *pb_dec_* are called field decoders. Each PB_LTYPE has an own field decoder, which handles translating from Protocol Buffers data to C data. The functions with names beginning with *pb_decode_* are used when dealing with callback fields. The typical reason for using callbacks is to have an array of unlimited size. In that case, `pb_decode`_ will call your callback function repeatedly, which can then store the values into e.g. filesystem in the order received in.
Each field decoder reads and decodes a single value. For arrays, the decoder is called repeatedly. For decoding numeric (including enumerated and boolean) values, use `pb_decode_varint`_, `pb_decode_svarint`_, `pb_decode_fixed32`_ and `pb_decode_fixed64`_. They take a pointer to a 32- or 64-bit C variable, which you may then cast to smaller datatype for storage.
You can use the decoders from your callbacks. Just be aware that the pb_field_t passed to the callback is not directly compatible For decoding strings and bytes fields, the length has already been decoded. You can therefore check the total length in *stream->state* and read the data using `pb_read`_.
with the *varint* field decoders. Instead, you must create a new pb_field_t structure and set the data_size according to the data type
you pass to *dest*, e.g. *field.data_size = sizeof(int);*. Other fields in the *pb_field_t* don't matter.
The field decoder interface is a bit messy as a result of the interface required inside the nanopb library. Finally, for decoding submessages in a callback, simply use `pb_decode`_ and pass it the *SubMessage_fields* descriptor array.
Eventually they may be replaced by separate wrapper functions with a more friendly interface.
pb_dec_varint pb_decode_varint
------------- ----------------
Field decoder for PB_LTYPE_VARINT. :: Read and decode a varint_ encoded integer. ::
bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
:stream: Input stream to read from. 1-10 bytes will be read. :stream: Input stream to read from. 1-10 bytes will be read.
:field: Field description structure. Only *field->data_size* matters. :dest: Storage for the decoded integer. Value is undefined on error.
:dest: Pointer to destination integer. Must have size of *field->data_size* bytes. :returns: True on success, false if value exceeds uint64_t range or an IO error happens.
:returns: True on success, false on IO errors or if `pb_decode_varint`_ fails.
This function first calls `pb_decode_varint`_. It then copies the first bytes of the 64-bit result value to *dest*, or on big endian architectures, the last bytes. pb_decode_svarint
-----------------
Similar to `pb_decode_varint`_, except that it performs zigzag-decoding on the value. This corresponds to the Protocol Buffers *sint32* and *sint64* datatypes. ::
pb_dec_svarint bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
--------------
Field decoder for PB_LTYPE_SVARINT. Similar to `pb_dec_varint`_, except that it performs zigzag-decoding on the value. ::
bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest); (parameters are the same as `pb_decode_varint`_)
(parameters are the same as `pb_dec_varint`_) pb_decode_fixed32
-----------------
Decode a *fixed32*, *sfixed32* or *float* value. ::
pb_dec_fixed32 bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
--------------
Field decoder for PB_LTYPE_FIXED32. ::
bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
:stream: Input stream to read from. 4 bytes will be read. :stream: Input stream to read from. 4 bytes will be read.
:field: Not used.
:dest: Pointer to destination *int32_t*, *uint32_t* or *float*. :dest: Pointer to destination *int32_t*, *uint32_t* or *float*.
:returns: True on success, false on IO errors. :returns: True on success, false on IO errors.
@@ -422,9 +405,9 @@ This function reads 4 bytes from the input stream.
On big endian architectures, it then reverses the order of the bytes. On big endian architectures, it then reverses the order of the bytes.
Finally, it writes the bytes to *dest*. Finally, it writes the bytes to *dest*.
pb_dec_fixed64 pb_decode_fixed64
-------------- -----------------
Field decoder for PB_LTYPE_FIXED64. :: Decode a *fixed64*, *sfixed64* or *double* value. ::
bool pb_dec_fixed(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_fixed(pb_istream_t *stream, const pb_field_t *field, void *dest);
@@ -433,53 +416,16 @@ Field decoder for PB_LTYPE_FIXED64. ::
:dest: Pointer to destination *int64_t*, *uint64_t* or *double*. :dest: Pointer to destination *int64_t*, *uint64_t* or *double*.
:returns: True on success, false on IO errors. :returns: True on success, false on IO errors.
Same as `pb_dec_fixed32`_, except this reads 8 bytes. Same as `pb_decode_fixed32`_, except this reads 8 bytes.
pb_dec_bytes pb_make_string_substream
------------ ------------------------
Field decoder for PB_LTYPE_BYTES. Reads a length-prefixed block of bytes. :: Decode the length for a field with wire type *PB_WT_STRING* and create a substream for reading the data. ::
bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
**Note:** This is an internal function that is not useful in decoder callbacks. To read bytes fields in callbacks, use :stream: Original input stream to read the length and data from.
*stream->bytes_left* and `pb_read`_. :substream: New substream that has limited length. Filled in by the function.
:returns: True on success, false if reading the length fails.
:stream: Input stream to read from.
:field: Field description structure. Only *field->data_size* matters.
:dest: Pointer to a structure similar to pb_bytes_array_t.
:returns: True on success, false on IO error or if length exceeds the array size.
This function expects a pointer to a structure with a *size_t* field at start, and a variable sized byte array after it. It will deduce the maximum size of the array from *field->data_size*.
pb_dec_string
-------------
Field decoder for PB_LTYPE_STRING. Reads a length-prefixed string. ::
bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
**Note:** This is an internal function that is not useful in decoder callbacks. To read string fields in callbacks, use
*stream->bytes_left* and `pb_read`_.
:stream: Input stream to read from.
:field: Field description structure. Only *field->data_size* matters.
:dest: Pointer to a character array of size *field->data_size*.
:returns: True on success, false on IO error or if length exceeds the array size.
This function null-terminates the string when successful. On error, the contents of the destination array is undefined.
pb_dec_submessage
-----------------
Field decoder for PB_LTYPE_SUBMESSAGE. Calls `pb_decode`_ to perform the actual decoding. ::
bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
**Note:** This is an internal function that is not useful in decoder callbacks. To read submessage fields in callbacks, use
`pb_decode`_ directly.
:stream: Input stream to read from.
:field: Field description structure. Only *field->ptr* matters.
:dest: Pointer to the destination structure.
:returns: True on success, false on IO error or if `pb_decode`_ fails.
The *field->ptr* should be a pointer to *pb_field_t* array describing the submessage.
This function uses `pb_decode_varint`_ to read an integer from the stream. This is interpreted as a number of bytes, and the substream is set up so that its `bytes_left` is initially the same as the length. The substream has a wrapper callback that in turn reads from the parent stream.

View File

@@ -44,10 +44,11 @@ const pb_field_t* decode_unionmessage_type(pb_istream_t *stream)
bool decode_unionmessage_contents(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) bool decode_unionmessage_contents(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
{ {
pb_field_t field = {}; /* NB: Could get rid of this wrapper by fixing issue #2. */ pb_istream_t substream;
field.ptr = fields; if (!pb_make_string_substream(stream, &substream))
return false;
return pb_dec_submessage(stream, &field, dest_struct); return pb_decode(&substream, fields, dest_struct);
} }
int main() int main()

View File

@@ -11,6 +11,7 @@
#define checkreturn __attribute__((warn_unused_result)) #define checkreturn __attribute__((warn_unused_result))
#endif #endif
#define NANOPB_INTERNALS
#include "pb.h" #include "pb.h"
#include "pb_decode.h" #include "pb_decode.h"
#include <string.h> #include <string.h>
@@ -192,7 +193,7 @@ static bool substream_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
return pb_read(parent, buf, count); return pb_read(parent, buf, count);
} }
static bool checkreturn make_string_substream(pb_istream_t *stream, pb_istream_t *substream) bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream)
{ {
uint32_t size; uint32_t size;
if (!pb_decode_varint32(stream, &size)) if (!pb_decode_varint32(stream, &size))
@@ -294,7 +295,7 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
/* Packed array */ /* Packed array */
size_t *size = (size_t*)iter->pSize; size_t *size = (size_t*)iter->pSize;
pb_istream_t substream; pb_istream_t substream;
if (!make_string_substream(stream, &substream)) if (!pb_make_string_substream(stream, &substream))
return false; return false;
while (substream.bytes_left && *size < iter->current->array_size) while (substream.bytes_left && *size < iter->current->array_size)
@@ -329,7 +330,7 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
{ {
pb_istream_t substream; pb_istream_t substream;
if (!make_string_substream(stream, &substream)) if (!pb_make_string_substream(stream, &substream))
return false; return false;
while (substream.bytes_left) while (substream.bytes_left)
@@ -467,78 +468,104 @@ bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void
/* Field decoders */ /* Field decoders */
/* Copy destsize bytes from src so that values are casted properly. bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest)
* On little endian machine, copy first n bytes of src
* On big endian machine, copy last n bytes of src
* srcsize must always be larger than destsize
*/
static void endian_copy(void *dest, void *src, size_t destsize, size_t srcsize)
{ {
#ifdef __BIG_ENDIAN__ uint64_t value;
memcpy(dest, (char*)src + (srcsize - destsize), destsize); if (!pb_decode_varint(stream, &value))
#else return false;
UNUSED(srcsize);
memcpy(dest, src, destsize); if (value & 1)
#endif *dest = ~(value >> 1);
else
*dest = value >> 1;
return true;
}
bool pb_decode_fixed32(pb_istream_t *stream, void *dest)
{
#ifdef __BIG_ENDIAN__
uint8_t *bytes = (uint8_t*)dest;
uint8_t lebytes[4];
if (!pb_read(stream, lebytes, 4))
return false;
bytes[0] = lebytes[3];
bytes[1] = lebytes[2];
bytes[2] = lebytes[1];
bytes[3] = lebytes[0];
return true;
#else
return pb_read(stream, (uint8_t*)dest, 4);
#endif
}
bool pb_decode_fixed64(pb_istream_t *stream, void *dest)
{
#ifdef __BIG_ENDIAN__
uint8_t *bytes = (uint8_t*)dest;
uint8_t lebytes[8];
if (!pb_read(stream, lebytes, 8))
return false;
bytes[0] = lebytes[7];
bytes[1] = lebytes[6];
bytes[2] = lebytes[5];
bytes[3] = lebytes[4];
bytes[4] = lebytes[3];
bytes[5] = lebytes[2];
bytes[6] = lebytes[1];
bytes[7] = lebytes[0];
return true;
#else
return pb_read(stream, (uint8_t*)dest, 8);
#endif
} }
bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
uint64_t temp; uint64_t value;
bool status = pb_decode_varint(stream, &temp); bool status = pb_decode_varint(stream, &value);
endian_copy(dest, &temp, field->data_size, sizeof(temp));
switch (field->data_size)
{
case 1: *(uint8_t*)dest = value; break;
case 2: *(uint16_t*)dest = value; break;
case 4: *(uint32_t*)dest = value; break;
case 8: *(uint64_t*)dest = value; break;
default: return false;
}
return status; return status;
} }
bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest) bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
uint64_t temp; int64_t value;
bool status = pb_decode_varint(stream, &temp); bool status = pb_decode_svarint(stream, &value);
temp = (temp >> 1) ^ -(int64_t)(temp & 1);
endian_copy(dest, &temp, field->data_size, sizeof(temp)); switch (field->data_size)
{
case 4: *(int32_t*)dest = value; break;
case 8: *(int64_t*)dest = value; break;
default: return false;
}
return status; return status;
} }
bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest) bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
#ifdef __BIG_ENDIAN__
uint8_t bytes[4] = {0};
bool status = pb_read(stream, bytes, 4);
if (status) {
uint8_t *d = (uint8_t*)dest;
d[0] = bytes[3];
d[1] = bytes[2];
d[2] = bytes[1];
d[3] = bytes[0];
}
return status;
#else
UNUSED(field); UNUSED(field);
return pb_read(stream, (uint8_t*)dest, 4); return pb_decode_fixed32(stream, dest);
#endif
} }
bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest) bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
#ifdef __BIG_ENDIAN__
uint8_t bytes[8] = {0};
bool status = pb_read(stream, bytes, 8);
if (status) {
uint8_t *d = (uint8_t*)dest;
d[0] = bytes[7];
d[1] = bytes[6];
d[2] = bytes[5];
d[3] = bytes[4];
d[4] = bytes[3];
d[5] = bytes[2];
d[6] = bytes[1];
d[7] = bytes[0];
}
return status;
#else
UNUSED(field); UNUSED(field);
return pb_read(stream, (uint8_t*)dest, 8); return pb_decode_fixed64(stream, dest);
#endif
} }
bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest) bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
@@ -577,7 +604,7 @@ bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field
{ {
pb_istream_t substream; pb_istream_t substream;
if (!make_string_substream(stream, &substream)) if (!pb_make_string_substream(stream, &substream))
return false; return false;
if (field->ptr == NULL) if (field->ptr == NULL)

View File

@@ -44,20 +44,38 @@ bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struc
* You may want to use these from your caller or callbacks. * You may want to use these from your caller or callbacks.
*/ */
/* Decode the tag for the next field in the stream. Gives the wire type and
* field tag. At end of the message, returns false and sets eof to true. */
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
/* Skip the field payload data, given the wire type. */
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
/* Decode an integer in the varint format. This works for bool, enum, int32,
* int64, uint32 and uint64 field types. */
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
bool pb_skip_varint(pb_istream_t *stream); /* Decode an integer in the zig-zagged svarint format. This works for sint32
bool pb_skip_string(pb_istream_t *stream); * and sint64. */
bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
/* --- Field decoders --- /* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
* Each decoder takes stream and field description, and a pointer to the field * a 4-byte wide C variable. */
* in the destination struct (dest = struct_addr + field->data_offset). bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
* For arrays, these functions are called repeatedly.
/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
* a 8-byte wide C variable. */
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
/* Make a limited-length substream for reading a PB_WT_STRING field. */
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
/* --- Internal functions ---
* These functions are not terribly useful for the average library user, but
* are exported to make the unit testing and extending nanopb easier.
*/ */
#ifdef NANOPB_INTERNALS
bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
@@ -67,4 +85,8 @@ bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_skip_varint(pb_istream_t *stream);
bool pb_skip_string(pb_istream_t *stream);
#endif
#endif #endif

View File

@@ -1,3 +1,5 @@
#define NANOPB_INTERNALS
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "pb_decode.h" #include "pb_decode.h"

View File

@@ -37,7 +37,7 @@ bool print_int32(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool print_fixed32(pb_istream_t *stream, const pb_field_t *field, void *arg) bool print_fixed32(pb_istream_t *stream, const pb_field_t *field, void *arg)
{ {
uint32_t value; uint32_t value;
if (!pb_dec_fixed32(stream, NULL, &value)) if (!pb_decode_fixed32(stream, &value))
return false; return false;
printf((char*)arg, (long)value); printf((char*)arg, (long)value);
@@ -47,7 +47,7 @@ bool print_fixed32(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool print_fixed64(pb_istream_t *stream, const pb_field_t *field, void *arg) bool print_fixed64(pb_istream_t *stream, const pb_field_t *field, void *arg)
{ {
uint64_t value; uint64_t value;
if (!pb_dec_fixed64(stream, NULL, &value)) if (!pb_decode_fixed64(stream, &value))
return false; return false;
printf((char*)arg, (long long)value); printf((char*)arg, (long long)value);