summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>2012-08-24 20:23:25 +0300
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>2012-08-24 20:23:25 +0300
commit2941e90e041b0d7872a4ea458e77dcc787ca6ff2 (patch)
treeca2f8b83d059e4ac5569413498aa27230ba2083c
parentdc2da0edc568b29361479fb7405c96b1a13442cf (diff)
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
-rw-r--r--docs/reference.rst118
-rw-r--r--example_unions/decode.c7
-rw-r--r--pb_decode.c137
-rw-r--r--pb_decode.h34
-rw-r--r--tests/decode_unittests.c2
-rw-r--r--tests/test_decode_callbacks.c4
6 files changed, 150 insertions, 152 deletions
diff --git a/docs/reference.rst b/docs/reference.rst
index 3331c6d3..7dd08edd 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -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.
-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
--------------
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.
: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
- 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.
+ 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`_.
- The field decoder interface is a bit messy as a result of the interface required inside the nanopb library.
- Eventually they may be replaced by separate wrapper functions with a more friendly interface.
+ Finally, for decoding submessages in a callback, simply use `pb_decode`_ and pass it the *SubMessage_fields* descriptor array.
-pb_dec_varint
--------------
-Field decoder for PB_LTYPE_VARINT. ::
+pb_decode_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.
-:field: Field description structure. Only *field->data_size* matters.
-:dest: Pointer to destination integer. Must have size of *field->data_size* bytes.
-: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.
+: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_dec_svarint
---------------
-Field decoder for PB_LTYPE_SVARINT. Similar to `pb_dec_varint`_, except that it performs zigzag-decoding on the value. ::
+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. ::
- bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+ bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
-(parameters are the same as `pb_dec_varint`_)
+(parameters are the same as `pb_decode_varint`_)
-pb_dec_fixed32
---------------
-Field decoder for PB_LTYPE_FIXED32. ::
+pb_decode_fixed32
+-----------------
+Decode a *fixed32*, *sfixed32* or *float* value. ::
- bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
+ bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
:stream: Input stream to read from. 4 bytes will be read.
-:field: Not used.
:dest: Pointer to destination *int32_t*, *uint32_t* or *float*.
: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.
Finally, it writes the bytes to *dest*.
-pb_dec_fixed64
---------------
-Field decoder for PB_LTYPE_FIXED64. ::
+pb_decode_fixed64
+-----------------
+Decode a *fixed64*, *sfixed64* or *double* value. ::
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*.
:returns: True on success, false on IO errors.
-Same as `pb_dec_fixed32`_, except this reads 8 bytes.
-
-pb_dec_bytes
-------------
-Field decoder for PB_LTYPE_BYTES. Reads a length-prefixed block of bytes. ::
-
- bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
+Same as `pb_decode_fixed32`_, except this reads 8 bytes.
-**Note:** This is an internal function that is not useful in decoder callbacks. To read bytes 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 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. ::
+pb_make_string_substream
+------------------------
+Decode the length for a field with wire type *PB_WT_STRING* and create a substream for reading the data. ::
- 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.
+ bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
-The *field->ptr* should be a pointer to *pb_field_t* array describing the submessage.
+:stream: Original input stream to read the length and data from.
+:substream: New substream that has limited length. Filled in by the function.
+:returns: True on success, false if reading the length fails.
+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.
diff --git a/example_unions/decode.c b/example_unions/decode.c
index edd568cb..a7cc7814 100644
--- a/example_unions/decode.c
+++ b/example_unions/decode.c
@@ -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)
{
- pb_field_t field = {}; /* NB: Could get rid of this wrapper by fixing issue #2. */
- field.ptr = fields;
+ pb_istream_t substream;
+ 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()
diff --git a/pb_decode.c b/pb_decode.c
index 9df4a894..59efb3d6 100644
--- a/pb_decode.c
+++ b/pb_decode.c
@@ -11,6 +11,7 @@
#define checkreturn __attribute__((warn_unused_result))
#endif
+#define NANOPB_INTERNALS
#include "pb.h"
#include "pb_decode.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);
}
-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;
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 */
size_t *size = (size_t*)iter->pSize;
pb_istream_t substream;
- if (!make_string_substream(stream, &substream))
+ if (!pb_make_string_substream(stream, &substream))
return false;
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;
- if (!make_string_substream(stream, &substream))
+ if (!pb_make_string_substream(stream, &substream))
return false;
while (substream.bytes_left)
@@ -467,78 +468,104 @@ bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void
/* Field decoders */
-/* Copy destsize bytes from src so that values are casted properly.
- * 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)
+bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest)
{
-#ifdef __BIG_ENDIAN__
- memcpy(dest, (char*)src + (srcsize - destsize), destsize);
-#else
- UNUSED(srcsize);
- memcpy(dest, src, destsize);
-#endif
+ uint64_t value;
+ if (!pb_decode_varint(stream, &value))
+ return false;
+
+ if (value & 1)
+ *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)
{
- uint64_t temp;
- bool status = pb_decode_varint(stream, &temp);
- endian_copy(dest, &temp, field->data_size, sizeof(temp));
+ uint64_t value;
+ bool status = pb_decode_varint(stream, &value);
+
+ 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;
}
bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
{
- uint64_t temp;
- bool status = pb_decode_varint(stream, &temp);
- temp = (temp >> 1) ^ -(int64_t)(temp & 1);
- endian_copy(dest, &temp, field->data_size, sizeof(temp));
+ int64_t value;
+ bool status = pb_decode_svarint(stream, &value);
+
+ switch (field->data_size)
+ {
+ case 4: *(int32_t*)dest = value; break;
+ case 8: *(int64_t*)dest = value; break;
+ default: return false;
+ }
+
return status;
}
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);
- return pb_read(stream, (uint8_t*)dest, 4);
-#endif
+ return pb_decode_fixed32(stream, 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);
- return pb_read(stream, (uint8_t*)dest, 8);
-#endif
+ return pb_decode_fixed64(stream, 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;
- if (!make_string_substream(stream, &substream))
+ if (!pb_make_string_substream(stream, &substream))
return false;
if (field->ptr == NULL)
diff --git a/pb_decode.h b/pb_decode.h
index 9cc67e8e..27535c10 100644
--- a/pb_decode.h
+++ b/pb_decode.h
@@ -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.
*/
+/* 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);
+
+/* Skip the field payload data, given the 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_skip_varint(pb_istream_t *stream);
-bool pb_skip_string(pb_istream_t *stream);
+/* Decode an integer in the zig-zagged svarint format. This works for sint32
+ * and sint64. */
+bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
+
+/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
+ * a 4-byte wide C variable. */
+bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
+
+/* 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);
-/* --- Field decoders ---
- * Each decoder takes stream and field description, and a pointer to the field
- * in the destination struct (dest = struct_addr + field->data_offset).
- * For arrays, these functions are called repeatedly.
+/* 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_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);
@@ -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_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
diff --git a/tests/decode_unittests.c b/tests/decode_unittests.c
index 6ba6d4f2..039c9fa4 100644
--- a/tests/decode_unittests.c
+++ b/tests/decode_unittests.c
@@ -1,3 +1,5 @@
+#define NANOPB_INTERNALS
+
#include <stdio.h>
#include <string.h>
#include "pb_decode.h"
diff --git a/tests/test_decode_callbacks.c b/tests/test_decode_callbacks.c
index 714b7bb2..95824d1a 100644
--- a/tests/test_decode_callbacks.c
+++ b/tests/test_decode_callbacks.c
@@ -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)
{
uint32_t value;
- if (!pb_dec_fixed32(stream, NULL, &value))
+ if (!pb_decode_fixed32(stream, &value))
return false;
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)
{
uint64_t value;
- if (!pb_dec_fixed64(stream, NULL, &value))
+ if (!pb_decode_fixed64(stream, &value))
return false;
printf((char*)arg, (long long)value);