Compare commits

...

22 Commits

Author SHA1 Message Date
Petteri Aimonen
d8bddabb83 Fix bug with .proto without messages (again), and add a test case for it. 2012-07-05 20:02:06 +03:00
Petteri Aimonen
01a1556898 Apparently some compilers don't want to automatically cast size_t to uint64_t. 2012-07-05 18:27:07 +03:00
Petteri Aimonen
efef38cf78 Fix some typos in __BIG_ENDIAN__ code 2012-07-05 18:24:11 +03:00
Petteri Aimonen
72cca8d783 Replace #warning with the standard #error. 2012-07-05 18:19:38 +03:00
Stan Hu
7c5bb6541a Cast enum vaules to integers and cast them back where appropraite
to prevent mixed enumeration type compiler warnings
2012-07-05 18:13:50 +03:00
Petteri Aimonen
9b1e1b440a Replace PB_MANY_FIELDS with PB_FIELD_16BIT and PB_FIELD_32BIT.
This allows more precise control over the memory use vs. field size.
2012-07-01 10:15:37 +03:00
Petteri Aimonen
78086cc27d Add PB_MANY_FIELDS option for supporting fields > 255.
Add generator warning if this is necessary.
Fixes issue #14.
2012-06-30 19:28:49 +03:00
Petteri Aimonen
67add3259a Warn if PB_MAX_REQUIRED_FIELDS is not large enough. 2012-06-30 18:23:18 +03:00
Petteri Aimonen
95eb4a5499 Improve the detection of missing required fields.
Now the limit of tracked fields is configurable at compile-time using
PB_MAX_REQUIRED_FIELDS. Added related test and updated documentation.

Fixes issue #18.
2012-06-30 18:10:08 +03:00
Petteri Aimonen
7bcf7ef579 Fixed formatting in docs 2012-06-25 22:17:13 +03:00
Petteri Aimonen
99375a132f Documented NANOPB_INTERNALS compilation option 2012-06-25 22:08:05 +03:00
Petteri Aimonen
c07e576de8 Note about __BIG_ENDIAN__ compilation option 2012-06-25 21:45:40 +03:00
Petteri Aimonen
3a919ddc5e Fix error when .proto contains no Messages (e.g. just enums).
Thanks to Paul Fertser for reporting this bug.
2012-06-20 21:31:23 +03:00
Petteri Aimonen
0f1d5cca59 Added example on how to handle unions. 2012-06-16 14:08:40 +03:00
Petteri Aimonen
e18352d506 Added new functions to public interface in pb_decode.h.
pb_decode_tag and pb_skip_field allow manually iterating the fields
in a message.
2012-06-16 14:07:37 +03:00
Petteri Aimonen
7e1059628c Fix non-constant initializer errors with some compilers.
Fixes issue #13. Thanks to Kevin Worth for reporting.
2012-06-13 21:43:40 +03:00
Petteri Aimonen
5af2c97ecd Add descriptor.proto into the #include exclusion list 2012-06-12 17:51:08 +03:00
Petteri Aimonen
feddc0115c Merge branch 'master' of https://code.google.com/p/nanopb 2012-05-19 21:25:47 +03:00
Petteri Aimonen
f9501ca185 Add better error messages in the generator when libraries cannot be imported.
Fixes issue #5.
2012-05-19 21:25:10 +03:00
Petteri Aimonen
b943290886 Fix 64-bitness warnings in the example.
Fixes issues 9 and 10.
2012-05-19 21:17:24 +03:00
Petteri Aimonen
d1ca88d20e Fixing compiler warnings, mostly related to unused parameters.
Thanks to David Hotham for the patch. Fixes issue 8.
2012-04-18 20:15:36 +03:00
Petteri Aimonen
9fbe9a5de3 Refactoring the field encoder interface.
Replaced the confusing pb_enc_* functions with new pb_encode_* functions that
have a cleaner interface. Updated documentation.

Got rid of the endian_copy stuff in pb_encode.c, instead using C casts to do it automatically.
This makes the code safer and also reduces binary size by about 5%.

Fixes Issue 6.
2012-03-01 13:46:52 +02:00
20 changed files with 908 additions and 266 deletions

View File

@@ -6,6 +6,26 @@ Nanopb: API reference
.. contents :: .. contents ::
Compilation options
===================
The following options can be specified using -D switch given to the C compiler:
============================ ================================================================================================
__BIG_ENDIAN__ Set this if your platform stores integers and floats in big-endian format.
Mixed-endian systems (different layout for ints and floats) are currently not supported.
NANOPB_INTERNALS Set this to expose the field encoder functions that are hidden since nanopb-0.1.3.
PB_MAX_REQUIRED_FIELDS Maximum number of required fields to check for presence. Default value is 64. Increases stack
usage 1 byte per every 8 fields. Compiler warning will tell if you need this.
PB_FIELD_16BIT Add support for tag numbers > 255 and fields larger than 255 bytes or 255 array entries.
Increases code size 3 bytes per each field. Compiler error will tell if you need this.
PB_FIELD_32BIT Add support for tag numbers > 65535 and fields larger than 65535 bytes or 65535 array entries.
Increases code size 9 bytes per each field. Compiler error will tell if you need this.
============================ ================================================================================================
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow raising some datatype limits to suit larger messages.
Their need is recognized automatically by C-preprocessor #if-directives in the generated .pb.h files. The default setting is to use
the smallest datatypes (least resources used).
pb.h pb.h
==== ====
@@ -66,7 +86,7 @@ Describes a single structure field with memory position in relation to others. T
:array_size: Maximum number of entries in an array, if it is an array type. :array_size: Maximum number of entries in an array, if it is an array type.
:ptr: Pointer to default value for optional fields, or to submessage description for PB_LTYPE_SUBMESSAGE. :ptr: Pointer to default value for optional fields, or to submessage description for PB_LTYPE_SUBMESSAGE.
The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will warn "Initializer too large for type" if the limits are exceeded. The types can be changed to larger ones if necessary. The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will give error if the values are too large. The types can be changed to larger ones by defining *PB_FIELD_16BIT*.
pb_bytes_array_t pb_bytes_array_t
---------------- ----------------
@@ -149,17 +169,13 @@ Encodes the contents of a structure as a protocol buffers message and writes it
Normally pb_encode simply walks through the fields description array and serializes each field in turn. However, submessages must be serialized twice: first to calculate their size and then to actually write them to output. This causes some constraints for callback fields, which must return the same data on every call. Normally pb_encode simply walks through the fields description array and serializes each field in turn. However, submessages must be serialized twice: first to calculate their size and then to actually write them to output. This causes some constraints for callback fields, which must return the same data on every call.
pb_encode_varint .. sidebar:: Encoding fields manually
----------------
Encodes an unsigned integer in the varint_ format. ::
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); The functions with names *pb_encode_\** 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_encode`_ will call your callback function, which in turn will call *pb_encode_\** functions repeatedly to write out values.
:stream: Output stream to write to. 1-10 bytes will be written. The tag of a field must be encoded separately with `pb_encode_tag_for_field`_. After that, you can call exactly one of the content-writing functions to encode the payload of the field. For repeated fields, you can repeat this process multiple times.
:value: Value to encode.
:returns: True on success, false on IO error.
.. _varint: http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints Writing packed arrays is a little bit more involved: you need to use `pb_encode_tag` and specify `PB_WT_STRING` as the wire type. Then you need to know exactly how much data you are going to write, and use `pb_encode_varint`_ to write out the number of bytes before writing the actual data. Substreams can be used to determine the number of bytes beforehand; see `pb_encode_submessage`_ source code for an example.
pb_encode_tag pb_encode_tag
------------- -------------
@@ -169,7 +185,7 @@ Starts a field in the Protocol Buffers binary format: encodes the field number a
:stream: Output stream to write to. 1-5 bytes will be written. :stream: Output stream to write to. 1-5 bytes will be written.
:wiretype: PB_WT_VARINT, PB_WT_64BIT, PB_WT_STRING or PB_WT_32BIT :wiretype: PB_WT_VARINT, PB_WT_64BIT, PB_WT_STRING or PB_WT_32BIT
:field_number: Identifier for the field, defined in the .proto file. :field_number: Identifier for the field, defined in the .proto file. You can get it from field->tag.
:returns: True on success, false on IO error. :returns: True on success, false on IO error.
pb_encode_tag_for_field pb_encode_tag_for_field
@@ -195,107 +211,71 @@ STRING, BYTES, SUBMESSAGE PB_WT_STRING
FIXED32 PB_WT_32BIT FIXED32 PB_WT_32BIT
========================= ============ ========================= ============
pb_encode_varint
----------------
Encodes a signed or unsigned integer in the varint_ format. Works for fields of type `bool`, `enum`, `int32`, `int64`, `uint32` and `uint64`::
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
:stream: Output stream to write to. 1-10 bytes will be written.
:value: Value to encode. Just cast e.g. int32_t directly to uint64_t.
:returns: True on success, false on IO error.
.. _varint: http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints
pb_encode_svarint
-----------------
Encodes a signed integer in the 'zig-zagged' format. Works for fields of type `sint32` and `sint64`::
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
(parameters are the same as for `pb_encode_varint`_
pb_encode_string pb_encode_string
---------------- ----------------
Writes the length of a string as varint and then contents of the string. Used for writing fields with wire type PB_WT_STRING. :: Writes the length of a string as varint and then contents of the string. Works for fields of type `bytes` and `string`::
bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size); bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size);
:stream: Output stream to write to. :stream: Output stream to write to.
:buffer: Pointer to string data. :buffer: Pointer to string data.
:size: Number of bytes in the string. :size: Number of bytes in the string. Pass `strlen(s)` for strings.
:returns: True on success, false on IO error. :returns: True on success, false on IO error.
.. sidebar:: Field encoders pb_encode_fixed32
The functions with names beginning with *pb_enc_* are called field encoders. Each PB_LTYPE has an own field encoder, which handles translating from C data into Protocol Buffers data.
By using the *data_size* in the field description and by taking advantage of C casting rules, it has been possible to combine many data types to a single LTYPE. For example, *int32*, *uint32*, *int64*, *uint64*, *bool* and *enum* are all handled by *pb_enc_varint*.
Each field encoder only encodes the contents of the field. The tag must be encoded separately with `pb_encode_tag_for_field`_.
You can use the field encoders from your callbacks. Just be aware that the pb_field_t passed to the callback is not directly compatible with most of the encoders. Instead, you must create a new pb_field_t structure and set the data_size according to the data type you pass to *src*.
pb_enc_varint
-------------
Field encoder for PB_LTYPE_VARINT. Takes the first *field->data_size* bytes from src, casts them as *uint64_t* and calls `pb_encode_varint`_. ::
bool pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
:stream: Output stream to write to.
:field: Field description structure. Only *data_size* matters.
:src: Pointer to start of the field data.
:returns: True on success, false on IO error.
pb_enc_svarint
--------------
Field encoder for PB_LTYPE_SVARINT. Similar to `pb_enc_varint`_, except first zig-zag encodes the value for more efficient negative number encoding. ::
bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
(parameters are the same as for `pb_enc_varint`_)
The number is considered negative if the high-order bit of the value is set. On big endian computers, it is the highest bit of *\*src*. On little endian computers, it is the highest bit of *\*(src + field->data_size - 1)*.
pb_enc_fixed32
--------------
Field encoder for PB_LTYPE_FIXED32. Writes the data in little endian order. On big endian computers, reverses the order of bytes. ::
bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
:stream: Output stream to write to.
:field: Not used.
:src: Pointer to start of the field data.
:returns: True on success, false on IO error.
pb_enc_fixed64
--------------
Field encoder for PB_LTYPE_FIXED64. Writes the data in little endian order. On big endian computers, reverses the order of bytes. ::
bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
(parameters are the same as for `pb_enc_fixed32`_)
The same function is used for both integers and doubles. This breaks encoding of double values on architectures where they are mixed endian (primarily some arm processors with hardware FPU).
pb_enc_bytes
------------
Field encoder for PB_LTYPE_BYTES. Just calls `pb_encode_string`_. ::
bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
:stream: Output stream to write to.
:field: Not used.
:src: Pointer to a structure similar to pb_bytes_array_t.
:returns: True on success, false on IO error.
This function expects a pointer to a structure with a *size_t* field at start, and a variable sized byte array after it. The platform-specific field offset is inferred from *pb_bytes_array_t*, which has a byte array of size 1.
pb_enc_string
-------------
Field encoder for PB_LTYPE_STRING. Determines size of string with strlen() and then calls `pb_encode_string`_. ::
bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
:stream: Output stream to write to.
:field: Not used.
:src: Pointer to a null-terminated string.
:returns: True on success, false on IO error.
pb_enc_submessage
----------------- -----------------
Field encoder for PB_LTYPE_SUBMESSAGE. Calls `pb_encode`_ to perform the actual encoding. :: Writes 4 bytes to stream and swaps bytes on big-endian architectures. Works for fields of type `fixed32`, `sfixed32` and `float`::
bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
:stream: Output stream to write to. :stream: Output stream to write to.
:field: Field description structure. The *ptr* field must be a pointer to a field description array for the submessage. :value: Pointer to a 4-bytes large C variable, for example `uint32_t foo;`.
:returns: True on success, false on IO error.
pb_encode_fixed64
-----------------
Writes 8 bytes to stream and swaps bytes on big-endian architecture. Works for fields of type `fixed64`, `sfixed64` and `double`::
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
:stream: Output stream to write to.
:value: Pointer to a 8-bytes large C variable, for example `uint64_t foo;`.
:returns: True on success, false on IO error.
pb_encode_submessage
--------------------
Encodes a submessage field, including the size header for it. Works for fields of any message type::
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
:stream: Output stream to write to.
:fields: Pointer to the autogenerated field description array for the submessage type, e.g. `MyMessage_fields`.
:src: Pointer to the structure where submessage data is. :src: Pointer to the structure where submessage data is.
:returns: True on success, false on IO errors, pb_encode errors or if submessage size changes between calls. :returns: True on success, false on IO errors, pb_encode errors or if submessage size changes between calls.
In Protocol Buffers format, the submessage size must be written before the submessage contents. Therefore, this function has to encode the submessage twice in order to know the size beforehand. In Protocol Buffers format, the submessage size must be written before the submessage contents. Therefore, this function has to encode the submessage twice in order to know the size beforehand.
If the submessage contains callback fields, the callback function might misbehave and write out a different amount of data on the second call. This situation is recognized and *false* is returned, but it is up to the caller to ensure that the receiver of the message does not interpret it as valid data. If the submessage contains callback fields, the callback function might misbehave and write out a different amount of data on the second call. This situation is recognized and *false* is returned, but garbage will be written to the output before the problem is detected.
pb_decode.h pb_decode.h
=========== ===========
@@ -323,6 +303,23 @@ Read data from input stream. Always use this function, don't try to call the str
End of file is signalled by *stream->bytes_left* being zero after pb_read returns false. End of file is signalled by *stream->bytes_left* being zero after pb_read returns false.
pb_decode
---------
Read and decode all fields of a structure. Reads until EOF on input stream. ::
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
:stream: Input stream to read from.
:fields: A field description array. Usually autogenerated.
:dest_struct: Pointer to structure where data will be stored.
:returns: True on success, false on IO error, on detectable errors in field description, if a field encoder returns false or if a required field is missing.
In Protocol Buffers binary format, EOF is only allowed between fields. If it happens anywhere else, pb_decode will return *false*. If pb_decode returns false, you cannot trust any of the data in the structure.
In addition to EOF, the pb_decode implementation supports terminating a message with a 0 byte. This is compatible with the official Protocol Buffers because 0 is never a valid field tag.
For optional fields, this function applies the default value and sets *has_<field>* to false if the field is not present.
pb_decode_varint pb_decode_varint
---------------- ----------------
Read and decode a varint_ encoded integer. :: Read and decode a varint_ encoded integer. ::
@@ -351,24 +348,30 @@ Skip a varint-length-prefixed string. This means skipping a value with wire type
:stream: Input stream to read from. :stream: Input stream to read from.
:returns: True on success, false on IO error or length exceeding uint32_t. :returns: True on success, false on IO error or length exceeding uint32_t.
pb_decode pb_decode_tag
--------- -------------
Read and decode all fields of a structure. Reads until EOF on input stream. :: Decode the tag that comes before field in the protobuf encoding::
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct); bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, int *tag, bool *eof);
:stream: Input stream to read from. :stream: Input stream to read from.
:fields: A field description array. Usually autogenerated. :wire_type: Pointer to variable where to store the wire type of the field.
:dest_struct: Pointer to structure where data will be stored. :tag: Pointer to variable where to store the tag of the field.
:returns: True on success, false on IO error, on detectable errors in field description, if a field encoder returns false or if a required field is missing. :eof: Pointer to variable where to store end-of-file status.
:returns: True on success, false on error or EOF.
In Protocol Buffers binary format, EOF is only allowed between fields. If it happens anywhere else, pb_decode will return *false*. If pb_decode returns false, you cannot trust any of the data in the structure. When the message (stream) ends, this function will return false and set *eof* to true. On other
errors, *eof* will be set to false.
In addition to EOF, the pb_decode implementation supports terminating a message with a 0 byte. This is compatible with the official Protocol Buffers because 0 is never a valid field tag. pb_skip_field
-------------
Remove the data for a field from the stream, without actually decoding it::
For optional fields, this function applies the default value and sets *has_<field>* to false if the field is not present. bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
Because of memory concerns, the detection of missing required fields is not perfect if the structure contains more than 32 fields. :stream: Input stream to read from.
:wire_type: Type of field to skip.
:returns: True on success, false on IO error.
.. sidebar:: Field decoders .. sidebar:: Field decoders

View File

@@ -30,7 +30,7 @@ bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void *arg
if (!pb_decode(stream, FileInfo_fields, &fileinfo)) if (!pb_decode(stream, FileInfo_fields, &fileinfo))
return false; return false;
printf("%-10lld %s\n", fileinfo.inode, fileinfo.name); printf("%-10lld %s\n", (long long)fileinfo.inode, fileinfo.name);
return true; return true;
} }

View File

@@ -10,13 +10,13 @@
static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{ {
int fd = (int)stream->state; int fd = (intptr_t)stream->state;
return send(fd, buf, count, 0) == count; return send(fd, buf, count, 0) == count;
} }
static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count) static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
{ {
int fd = (int)stream->state; int fd = (intptr_t)stream->state;
int result; int result;
if (buf == NULL) if (buf == NULL)
@@ -38,12 +38,12 @@ static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
pb_ostream_t pb_ostream_from_socket(int fd) pb_ostream_t pb_ostream_from_socket(int fd)
{ {
pb_ostream_t stream = {&write_callback, (void*)fd, SIZE_MAX, 0}; pb_ostream_t stream = {&write_callback, (void*)(intptr_t)fd, SIZE_MAX, 0};
return stream; return stream;
} }
pb_istream_t pb_istream_from_socket(int fd) pb_istream_t pb_istream_from_socket(int fd)
{ {
pb_istream_t stream = {&read_callback, (void*)fd, SIZE_MAX}; pb_istream_t stream = {&read_callback, (void*)(intptr_t)fd, SIZE_MAX};
return stream; return stream;
} }

View File

@@ -38,7 +38,7 @@ bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, const void
if (!pb_encode_tag_for_field(stream, field)) if (!pb_encode_tag_for_field(stream, field))
return false; return false;
if (!pb_enc_submessage(stream, field, &fileinfo)) if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
return false; return false;
} }

17
example_unions/Makefile Normal file
View File

@@ -0,0 +1,17 @@
CFLAGS=-ansi -Wall -Werror -I .. -g -O0
DEPS=../pb_decode.c ../pb_decode.h ../pb_encode.c ../pb_encode.h ../pb.h
all: encode decode
./encode 1 | ./decode
./encode 2 | ./decode
./encode 3 | ./decode
clean:
rm -f encode unionproto.pb.h unionproto.pb.c
%: %.c $(DEPS) unionproto.pb.h unionproto.pb.c
$(CC) $(CFLAGS) -o $@ $< ../pb_decode.c ../pb_encode.c unionproto.pb.c
unionproto.pb.h unionproto.pb.c: unionproto.proto ../generator/nanopb_generator.py
protoc -I. -I../generator -I/usr/include -ounionproto.pb $<
python ../generator/nanopb_generator.py unionproto.pb

92
example_unions/decode.c Normal file
View File

@@ -0,0 +1,92 @@
/* This program reads a message from stdin, detects its type and decodes it.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pb_decode.h>
#include "unionproto.pb.h"
/* This function reads manually the first tag from the stream and finds the
* corresponding message type. It doesn't yet decode the actual message.
*
* Returns a pointer to the MsgType_fields array, as an identifier for the
* message type. Returns null if the tag is of unknown type or an error occurs.
*/
const pb_field_t* decode_unionmessage_type(pb_istream_t *stream)
{
pb_wire_type_t wire_type;
int tag;
bool eof;
while (pb_decode_tag(stream, &wire_type, &tag, &eof))
{
if (wire_type == PB_WT_STRING)
{
const pb_field_t *field;
for (field = UnionMessage_fields; field->tag != 0; field++)
{
if (field->tag == tag && (field->type & PB_LTYPE_SUBMESSAGE))
{
/* Found our field. */
return field->ptr;
}
}
}
/* Wasn't our field.. */
pb_skip_field(stream, wire_type);
}
return NULL;
}
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;
return pb_dec_submessage(stream, &field, dest_struct);
}
int main()
{
/* Read the data into buffer */
uint8_t buffer[512];
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
pb_istream_t stream = pb_istream_from_buffer(buffer, count);
const pb_field_t *type = decode_unionmessage_type(&stream);
bool status = false;
if (type == MsgType1_fields)
{
MsgType1 msg = {};
status = decode_unionmessage_contents(&stream, MsgType1_fields, &msg);
printf("Got MsgType1: %d\n", msg.value);
}
else if (type == MsgType2_fields)
{
MsgType2 msg = {};
status = decode_unionmessage_contents(&stream, MsgType2_fields, &msg);
printf("Got MsgType2: %s\n", msg.value ? "true" : "false");
}
else if (type == MsgType3_fields)
{
MsgType3 msg = {};
status = decode_unionmessage_contents(&stream, MsgType3_fields, &msg);
printf("Got MsgType3: %d %d\n", msg.value1, msg.value2);
}
if (!status)
{
printf("Decoding failed.\n");
return 1;
}
return 0;
}

85
example_unions/encode.c Normal file
View File

@@ -0,0 +1,85 @@
/* This program takes a command line argument and encodes a message in
* one of MsgType1, MsgType2 or MsgType3.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pb_encode.h>
#include "unionproto.pb.h"
/* This function is the core of the union encoding process. It handles
* the top-level pb_field_t array manually, in order to encode a correct
* field tag before the message. The pointer to MsgType_fields array is
* used as an unique identifier for the message type.
*/
bool encode_unionmessage(pb_ostream_t *stream, const pb_field_t messagetype[], const void *message)
{
const pb_field_t *field;
for (field = UnionMessage_fields; field->tag != 0; field++)
{
if (field->ptr == messagetype)
{
/* This is our field, encode the message using it. */
if (!pb_encode_tag_for_field(stream, field))
return false;
return pb_encode_submessage(stream, messagetype, message);
}
}
/* Didn't find the field for messagetype */
return false;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s (1|2|3)\n", argv[0]);
return 1;
}
uint8_t buffer[512];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
bool status = false;
int msgtype = atoi(argv[1]);
if (msgtype == 1)
{
/* Send message of type 1 */
MsgType1 msg = {42};
status = encode_unionmessage(&stream, MsgType1_fields, &msg);
}
else if (msgtype == 2)
{
/* Send message of type 2 */
MsgType2 msg = {true};
status = encode_unionmessage(&stream, MsgType2_fields, &msg);
}
else if (msgtype == 3)
{
/* Send message of type 3 */
MsgType3 msg = {3, 1415};
status = encode_unionmessage(&stream, MsgType3_fields, &msg);
}
else
{
fprintf(stderr, "Unknown message type: %d\n", msgtype);
return 2;
}
if (!status)
{
fprintf(stderr, "Encoding failed!\n");
return 3;
}
else
{
fwrite(buffer, 1, stream.bytes_written, stdout);
return 0; /* Success */
}
}

View File

@@ -0,0 +1,30 @@
// This is an example of how to handle 'union' style messages
// with nanopb, without allocating memory for all the message types.
//
// There is no official type in Protocol Buffers for describing unions,
// but they are commonly implemented by filling out exactly one of
// several optional fields.
message MsgType1
{
required int32 value = 1;
}
message MsgType2
{
required bool value = 1;
}
message MsgType3
{
required int32 value1 = 1;
required int32 value2 = 2;
}
message UnionMessage
{
optional MsgType1 msg1 = 1;
optional MsgType2 msg2 = 2;
optional MsgType3 msg3 = 3;
}

View File

@@ -1,7 +1,27 @@
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
import google.protobuf.descriptor_pb2 as descriptor try:
import nanopb_pb2 import google.protobuf.descriptor_pb2 as descriptor
except:
print
print "*************************************************************"
print "*** Could not import the Google protobuf Python libraries ***"
print "*** Try installing package 'python-protobuf' or similar. ***"
print "*************************************************************"
print
raise
try:
import nanopb_pb2
except:
print
print "***************************************************************"
print "*** Could not import the precompiled nanopb_pb2.py. ***"
print "*** Run 'make' in the 'generator' folder to update the file.***"
print "***************************************************************"
print
raise
import os.path import os.path
# Values are tuple (c type, pb ltype) # Values are tuple (c type, pb ltype)
@@ -197,10 +217,10 @@ class Field:
prev_field_name is the name of the previous field or None. prev_field_name is the name of the previous field or None.
''' '''
result = ' {%d, ' % self.tag result = ' {%d, ' % self.tag
result += self.htype result += '(pb_type_t) ((int) ' + self.htype
if self.ltype is not None: if self.ltype is not None:
result += ' | ' + self.ltype result += ' | (int) ' + self.ltype
result += ',\n' result += '),\n'
if prev_field_name is None: if prev_field_name is None:
result += ' offsetof(%s, %s),' % (self.struct_name, self.name) result += ' offsetof(%s, %s),' % (self.struct_name, self.name)
@@ -232,6 +252,18 @@ class Field:
return result return result
def largest_field_value(self):
'''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly.
Returns numeric value or a C-expression for assert.'''
if self.ltype == 'PB_LTYPE_SUBMESSAGE':
if self.htype == 'PB_HTYPE_ARRAY':
return 'pb_membersize(%s, %s[0])' % (self.struct_name, self.name)
else:
return 'pb_membersize(%s, %s)' % (self.struct_name, self.name)
return max(self.tag, self.max_size, self.max_count)
class Message: class Message:
def __init__(self, names, desc): def __init__(self, names, desc):
self.name = names self.name = names
@@ -323,7 +355,7 @@ def toposort2(data):
''' '''
for k, v in data.items(): for k, v in data.items():
v.discard(k) # Ignore self dependencies v.discard(k) # Ignore self dependencies
extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys()) extra_items_in_deps = reduce(set.union, data.values(), set()) - set(data.keys())
data.update(dict([(item, set()) for item in extra_items_in_deps])) data.update(dict([(item, set()) for item in extra_items_in_deps]))
while True: while True:
ordered = set(item for item,dep in data.items() if not dep) ordered = set(item for item,dep in data.items() if not dep)
@@ -382,6 +414,51 @@ def generate_header(dependencies, headername, enums, messages):
for msg in messages: for msg in messages:
yield msg.fields_declaration() + '\n' yield msg.fields_declaration() + '\n'
if messages:
count_required_fields = lambda m: len([f for f in msg.fields if f.htype == 'PB_HTYPE_REQUIRED'])
largest_msg = max(messages, key = count_required_fields)
largest_count = count_required_fields(largest_msg)
if largest_count > 64:
yield '\n/* Check that missing required fields will be properly detected */\n'
yield '#if PB_MAX_REQUIRED_FIELDS < %d\n' % largest_count
yield '#error Properly detecting missing required fields in %s requires \\\n' % largest_msg.name
yield ' setting PB_MAX_REQUIRED_FIELDS to %d or more.\n' % largest_count
yield '#endif\n'
worst = 0
worst_field = ''
checks = []
for msg in messages:
for field in msg.fields:
status = field.largest_field_value()
if isinstance(status, (str, unicode)):
checks.append(status)
elif status > worst:
worst = status
worst_field = str(field.struct_name) + '.' + str(field.name)
if worst > 255 or checks:
yield '\n/* Check that field information fits in pb_field_t */\n'
if worst < 65536:
yield '#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)\n'
if worst > 255:
yield '#error Field descriptor for %s is too large. Define PB_FIELD_16BIT to fix this.\n' % worst_field
else:
assertion = ' && '.join(str(c) + ' < 256' for c in checks)
yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT)\n' % assertion
yield '#endif\n\n'
if worst > 65535 or checks:
yield '#if !defined(PB_FIELD_32BIT)\n'
if worst > 65535:
yield '#error Field descriptor for %s is too large. Define PB_FIELD_32BIT to fix this.\n' % worst_field
else:
assertion = ' && '.join(str(c) + ' < 65536' for c in checks)
yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT)\n' % assertion
yield '#endif\n'
# End of header
yield '\n#endif\n' yield '\n#endif\n'
def generate_source(headername, enums, messages): def generate_source(headername, enums, messages):
@@ -422,7 +499,7 @@ if __name__ == '__main__':
# List of .proto files that should not be included in the C header file # List of .proto files that should not be included in the C header file
# even if they are mentioned in the source .proto. # even if they are mentioned in the source .proto.
excludes = ['nanopb.proto'] excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto']
dependencies = [d for d in fdesc.file[0].dependency if d not in excludes] dependencies = [d for d in fdesc.file[0].dependency if d not in excludes]
header = open(headername, 'w') header = open(headername, 'w')

44
pb.h
View File

@@ -17,6 +17,26 @@
#define pb_packed #define pb_packed
#endif #endif
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif
/* Compile-time assertion, used for checking compatible compilation options. */
#ifndef STATIC_ASSERT
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1];
#endif
/* Number of required fields to keep track of
* (change here or on compiler command line). */
#ifndef PB_MAX_REQUIRED_FIELDS
#define PB_MAX_REQUIRED_FIELDS 64
#endif
#if PB_MAX_REQUIRED_FIELDS < 64
#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
#endif
/* List of possible field types. These are used in the autogenerated code. /* List of possible field types. These are used in the autogenerated code.
* Least-significant 4 bits tell the scalar type * Least-significant 4 bits tell the scalar type
* Most-significant 4 bits specify repeated/required/packed etc. * Most-significant 4 bits specify repeated/required/packed etc.
@@ -83,19 +103,37 @@ typedef enum {
/* This structure is used in auto-generated constants /* This structure is used in auto-generated constants
* to specify struct fields. * to specify struct fields.
* You can change field sizes here if you need structures * You can change field sizes if you need structures
* larger than 256 bytes or field tags larger than 256. * larger than 256 bytes or field tags larger than 256.
* The compiler should complain if your .proto has such * The compiler should complain if your .proto has such
* structures ("initializer too large for type"). * structures. Fix that by defining PB_FIELD_16BIT or
* PB_FIELD_32BIT.
*/ */
typedef struct _pb_field_t pb_field_t; typedef struct _pb_field_t pb_field_t;
struct _pb_field_t { struct _pb_field_t {
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
uint8_t tag; uint8_t tag;
pb_type_t type; pb_type_t type;
uint8_t data_offset; /* Offset of field data, relative to previous field. */ uint8_t data_offset; /* Offset of field data, relative to previous field. */
int8_t size_offset; /* Offset of array size or has-boolean, relative to data */ int8_t size_offset; /* Offset of array size or has-boolean, relative to data */
uint8_t data_size; /* Data size in bytes for a single item */ uint8_t data_size; /* Data size in bytes for a single item */
uint8_t array_size; /* Maximum number of entries in array */ uint8_t array_size; /* Maximum number of entries in array */
#elif defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
uint16_t tag;
pb_type_t type;
uint8_t data_offset;
int8_t size_offset;
uint16_t data_size;
uint16_t array_size;
#else
uint32_t tag;
pb_type_t type;
uint8_t data_offset;
int8_t size_offset;
uint32_t data_size;
uint32_t array_size;
#endif
/* Field definitions for submessage /* Field definitions for submessage
* OR default value for all other non-array, non-callback types * OR default value for all other non-array, non-callback types
@@ -156,7 +194,7 @@ typedef enum {
#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) #define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) #define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2))
#define pb_delta_end(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2)) #define pb_delta_end(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2))
#define PB_LAST_FIELD {0,0,0,0} #define PB_LAST_FIELD {0,(pb_type_t) 0,0,0}
#endif #endif

View File

@@ -75,7 +75,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
{ {
uint64_t temp; uint64_t temp;
bool status = pb_decode_varint(stream, &temp); bool status = pb_decode_varint(stream, &temp);
*dest = temp; *dest = (uint32_t)temp;
return status; return status;
} }
@@ -117,12 +117,33 @@ bool checkreturn pb_skip_string(pb_istream_t *stream)
return pb_read(stream, NULL, length); return pb_read(stream, NULL, length);
} }
/* Currently the wire type related stuff is kept hidden from bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, int *tag, bool *eof)
* callbacks. They shouldn't need it. It's better for performance {
* to just assume the correct type and fail safely on corrupt message. uint32_t temp;
*/ *eof = false;
*wire_type = 0;
*tag = 0;
static bool checkreturn skip(pb_istream_t *stream, pb_wire_type_t wire_type) if (!pb_decode_varint32(stream, &temp))
{
if (stream->bytes_left == 0)
*eof = true;
return false;
}
if (temp == 0)
{
*eof = true; /* Special feature: allow 0-terminated messages. */
return false;
}
*tag = temp >> 3;
*wire_type = (pb_wire_type_t)(temp & 7);
return true;
}
bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type)
{ {
switch (wire_type) switch (wire_type)
{ {
@@ -185,18 +206,20 @@ static bool checkreturn make_string_substream(pb_istream_t *stream, pb_istream_t
/* Iterator for pb_field_t list */ /* Iterator for pb_field_t list */
typedef struct { typedef struct {
const pb_field_t *start; const pb_field_t *start; /* Start of the pb_field_t array */
const pb_field_t *current; const pb_field_t *current; /* Current position of the iterator */
int field_index; int field_index; /* Zero-based index of the field. */
void *dest_struct; int required_field_index; /* Zero-based index that counts only the required fields */
void *pData; void *dest_struct; /* Pointer to the destination structure to decode to */
void *pSize; void *pData; /* Pointer where to store current field value */
void *pSize; /* Pointer where to store the size of current array field */
} pb_field_iterator_t; } pb_field_iterator_t;
static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct) static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct)
{ {
iter->start = iter->current = fields; iter->start = iter->current = fields;
iter->field_index = 0; iter->field_index = 0;
iter->required_field_index = 0;
iter->pData = (char*)dest_struct + iter->current->data_offset; iter->pData = (char*)dest_struct + iter->current->data_offset;
iter->pSize = (char*)iter->pData + iter->current->size_offset; iter->pSize = (char*)iter->pData + iter->current->size_offset;
iter->dest_struct = dest_struct; iter->dest_struct = dest_struct;
@@ -210,12 +233,16 @@ static bool pb_field_next(pb_field_iterator_t *iter)
if (PB_HTYPE(iter->current->type) == PB_HTYPE_ARRAY) if (PB_HTYPE(iter->current->type) == PB_HTYPE_ARRAY)
prev_size *= iter->current->array_size; prev_size *= iter->current->array_size;
if (PB_HTYPE(iter->current->type) == PB_HTYPE_REQUIRED)
iter->required_field_index++;
iter->current++; iter->current++;
iter->field_index++; iter->field_index++;
if (iter->current->tag == 0) if (iter->current->tag == 0)
{ {
iter->current = iter->start; iter->current = iter->start;
iter->field_index = 0; iter->field_index = 0;
iter->required_field_index = 0;
iter->pData = iter->dest_struct; iter->pData = iter->dest_struct;
prev_size = 0; prev_size = 0;
notwrapped = false; notwrapped = false;
@@ -292,7 +319,7 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
pb_callback_t *pCallback = (pb_callback_t*)iter->pData; pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
if (pCallback->funcs.decode == NULL) if (pCallback->funcs.decode == NULL)
return skip(stream, wire_type); return pb_skip_field(stream, wire_type);
if (wire_type == PB_WT_STRING) if (wire_type == PB_WT_STRING)
{ {
@@ -382,9 +409,8 @@ static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_str
bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
{ {
uint32_t fields_seen = 0; /* Used to check for required fields */ uint8_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 7) / 8] = {}; /* Used to check for required fields */
pb_field_iterator_t iter; pb_field_iterator_t iter;
int i;
pb_message_set_to_defaults(fields, dest_struct); pb_message_set_to_defaults(fields, dest_struct);
@@ -392,46 +418,46 @@ bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void
while (stream->bytes_left) while (stream->bytes_left)
{ {
uint32_t temp;
int tag; int tag;
pb_wire_type_t wire_type; pb_wire_type_t wire_type;
if (!pb_decode_varint32(stream, &temp)) bool eof;
if (!pb_decode_tag(stream, &wire_type, &tag, &eof))
{ {
if (stream->bytes_left == 0) if (eof)
break; /* It was EOF */ break;
else else
return false; /* It was error */ return false;
} }
if (temp == 0)
break; /* Special feature: allow 0-terminated messages. */
tag = temp >> 3;
wire_type = (pb_wire_type_t)(temp & 7);
if (!pb_field_find(&iter, tag)) if (!pb_field_find(&iter, tag))
{ {
/* No match found, skip data */ /* No match found, skip data */
if (!skip(stream, wire_type)) if (!pb_skip_field(stream, wire_type))
return false; return false;
continue; continue;
} }
fields_seen |= 1 << (iter.field_index & 31); if (PB_HTYPE(iter.current->type) == PB_HTYPE_REQUIRED
&& iter.required_field_index < PB_MAX_REQUIRED_FIELDS)
{
fields_seen[iter.required_field_index >> 3] |= 1 << (iter.required_field_index & 7);
}
if (!decode_field(stream, wire_type, &iter)) if (!decode_field(stream, wire_type, &iter))
return false; return false;
} }
/* Check that all required fields (mod 31) were present. */ /* Check that all required fields were present. */
for (i = 0; fields[i].tag != 0; i++) pb_field_init(&iter, fields, dest_struct);
{ do {
if (PB_HTYPE(fields[i].type) == PB_HTYPE_REQUIRED && if (PB_HTYPE(iter.current->type) == PB_HTYPE_REQUIRED &&
!(fields_seen & (1 << (i & 31)))) iter.required_field_index < PB_MAX_REQUIRED_FIELDS &&
!(fields_seen[iter.required_field_index >> 3] & (1 << (iter.required_field_index & 7))))
{ {
return false; return false;
} }
} } while (pb_field_next(&iter));
return true; return true;
} }
@@ -448,6 +474,7 @@ static void endian_copy(void *dest, void *src, size_t destsize, size_t srcsize)
#ifdef __BIG_ENDIAN__ #ifdef __BIG_ENDIAN__
memcpy(dest, (char*)src + (srcsize - destsize), destsize); memcpy(dest, (char*)src + (srcsize - destsize), destsize);
#else #else
UNUSED(srcsize);
memcpy(dest, src, destsize); memcpy(dest, src, destsize);
#endif #endif
} }
@@ -475,11 +502,15 @@ bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, v
uint8_t bytes[4] = {0}; uint8_t bytes[4] = {0};
bool status = pb_read(stream, bytes, 4); bool status = pb_read(stream, bytes, 4);
if (status) { if (status) {
uint8_t bebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]}; uint8_t *d = (uint8_t*)dest;
memcpy(dest, bebytes, 4); d[0] = bytes[3];
d[1] = bytes[2];
d[2] = bytes[1];
d[3] = bytes[0];
} }
return status; return status;
#else #else
UNUSED(field);
return pb_read(stream, (uint8_t*)dest, 4); return pb_read(stream, (uint8_t*)dest, 4);
#endif #endif
} }
@@ -490,12 +521,19 @@ bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, v
uint8_t bytes[8] = {0}; uint8_t bytes[8] = {0};
bool status = pb_read(stream, bytes, 8); bool status = pb_read(stream, bytes, 8);
if (status) { if (status) {
uint8_t bebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4], uint8_t *d = (uint8_t*)dest;
bytes[3], bytes[2], bytes[1], bytes[0]}; d[0] = bytes[7];
memcpy(dest, bebytes, 8); 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; return status;
#else #else
UNUSED(field);
return pb_read(stream, (uint8_t*)dest, 8); return pb_read(stream, (uint8_t*)dest, 8);
#endif #endif
} }
@@ -524,7 +562,7 @@ bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, vo
return false; return false;
/* Check length, noting the null terminator */ /* Check length, noting the null terminator */
if (size > field->data_size - 1) if (size + 1 > field->data_size)
return false; return false;
status = pb_read(stream, (uint8_t*)dest, size); status = pb_read(stream, (uint8_t*)dest, size);

View File

@@ -48,6 +48,9 @@ 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.
*/ */
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, int *tag, bool *eof);
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
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); bool pb_skip_varint(pb_istream_t *stream);

View File

@@ -3,6 +3,7 @@
* 2011 Petteri Aimonen <jpa@kapsi.fi> * 2011 Petteri Aimonen <jpa@kapsi.fi>
*/ */
#define NANOPB_INTERNALS
#include "pb.h" #include "pb.h"
#include "pb_encode.h" #include "pb_encode.h"
#include <string.h> #include <string.h>
@@ -75,7 +76,7 @@ bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
const void *pData, size_t count, pb_encoder_t func) const void *pData, size_t count, pb_encoder_t func)
{ {
int i; size_t i;
const void *p; const void *p;
size_t size; size_t size;
@@ -109,7 +110,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
size = sizestream.bytes_written; size = sizestream.bytes_written;
} }
if (!pb_encode_varint(stream, size)) if (!pb_encode_varint(stream, (uint64_t)size))
return false; return false;
if (stream->callback == NULL) if (stream->callback == NULL)
@@ -211,7 +212,7 @@ bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
while (value) while (value)
{ {
buffer[i] = (value & 0x7F) | 0x80; buffer[i] = (uint8_t)((value & 0x7F) | 0x80);
value >>= 7; value >>= 7;
i++; i++;
} }
@@ -220,10 +221,55 @@ bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
return pb_write(stream, buffer, i); return pb_write(stream, buffer, i);
} }
bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
{
uint64_t zigzagged;
if (value < 0)
zigzagged = ~(value << 1);
else
zigzagged = value << 1;
return pb_encode_varint(stream, zigzagged);
}
bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
{
#ifdef __BIG_ENDIAN__
const uint8_t *bytes = value;
uint8_t lebytes[4];
lebytes[0] = bytes[3];
lebytes[1] = bytes[2];
lebytes[2] = bytes[1];
lebytes[3] = bytes[0];
return pb_write(stream, lebytes, 4);
#else
return pb_write(stream, (uint8_t*)value, 4);
#endif
}
bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
{
#ifdef __BIG_ENDIAN__
const uint8_t *bytes = value;
uint8_t lebytes[8];
lebytes[0] = bytes[7];
lebytes[1] = bytes[6];
lebytes[2] = bytes[5];
lebytes[3] = bytes[4];
lebytes[4] = bytes[3];
lebytes[5] = bytes[2];
lebytes[6] = bytes[1];
lebytes[7] = bytes[0];
return pb_write(stream, lebytes, 8);
#else
return pb_write(stream, (uint8_t*)value, 8);
#endif
}
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number) bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number)
{ {
int tag = wiretype | (field_number << 3); int tag = wiretype | (field_number << 3);
return pb_encode_varint(stream, tag); return pb_encode_varint(stream, (uint64_t)tag);
} }
bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field) bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
@@ -259,105 +305,25 @@ bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t
bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size) bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size)
{ {
if (!pb_encode_varint(stream, size)) if (!pb_encode_varint(stream, (uint64_t)size))
return false; return false;
return pb_write(stream, buffer, size); return pb_write(stream, buffer, size);
} }
/* Field encoders */ bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
/* Copy srcsize bytes from src so that values are casted properly.
* On little endian machine, copy to start of dest
* On big endian machine, copy to end of dest
* destsize must always be larger than srcsize
*
* Note: This is the reverse of the endian_copy in pb_decode.c.
*/
static void endian_copy(void *dest, const void *src, size_t destsize, size_t srcsize)
{
#ifdef __BIG_ENDIAN__
memcpy((char*)dest + (destsize - srcsize), src, srcsize);
#else
memcpy(dest, src, srcsize);
#endif
}
bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
uint64_t value = 0;
endian_copy(&value, src, sizeof(value), field->data_size);
return pb_encode_varint(stream, value);
}
bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
uint64_t value = 0;
uint64_t zigzagged;
uint64_t signbitmask, xormask;
endian_copy(&value, src, sizeof(value), field->data_size);
signbitmask = (uint64_t)0x80 << (field->data_size * 8 - 8);
xormask = ((uint64_t)-1) >> (64 - field->data_size * 8);
if (value & signbitmask)
zigzagged = ((value ^ xormask) << 1) | 1;
else
zigzagged = value << 1;
return pb_encode_varint(stream, zigzagged);
}
bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
#ifdef __BIG_ENDIAN__
uint8_t bytes[8] = {0};
memcpy(bytes, src, 8);
uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4],
bytes[3], bytes[2], bytes[1], bytes[0]};
return pb_write(stream, lebytes, 8);
#else
return pb_write(stream, (uint8_t*)src, 8);
#endif
}
bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
#ifdef __BIG_ENDIAN__
uint8_t bytes[4] = {0};
memcpy(bytes, src, 4);
uint8_t lebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
return pb_write(stream, lebytes, 4);
#else
return pb_write(stream, (uint8_t*)src, 4);
#endif
}
bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
pb_bytes_array_t *bytes = (pb_bytes_array_t*)src;
return pb_encode_string(stream, bytes->bytes, bytes->size);
}
bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
return pb_encode_string(stream, (uint8_t*)src, strlen((char*)src));
}
bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
/* First calculate the message size using a non-writing substream. */
pb_ostream_t substream = {0}; pb_ostream_t substream = {0};
size_t size; size_t size;
bool status; bool status;
if (field->ptr == NULL) if (!pb_encode(&substream, fields, src_struct))
return false;
if (!pb_encode(&substream, (pb_field_t*)field->ptr, src))
return false; return false;
size = substream.bytes_written; size = substream.bytes_written;
if (!pb_encode_varint(stream, size)) if (!pb_encode_varint(stream, (uint64_t)size))
return false; return false;
if (stream->callback == NULL) if (stream->callback == NULL)
@@ -373,7 +339,7 @@ bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field
substream.max_size = size; substream.max_size = size;
substream.bytes_written = 0; substream.bytes_written = 0;
status = pb_encode(&substream, (pb_field_t*)field->ptr, src); status = pb_encode(&substream, fields, src_struct);
stream->bytes_written += substream.bytes_written; stream->bytes_written += substream.bytes_written;
stream->state = substream.state; stream->state = substream.state;
@@ -384,3 +350,68 @@ bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field
return status; return status;
} }
/* Field encoders */
bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
uint64_t value = 0;
switch (field->data_size)
{
case 1: value = *(uint8_t*)src; break;
case 2: value = *(uint16_t*)src; break;
case 4: value = *(uint32_t*)src; break;
case 8: value = *(uint64_t*)src; break;
default: return false;
}
return pb_encode_varint(stream, value);
}
bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
uint64_t value = 0;
switch (field->data_size)
{
case 4: value = *(int32_t*)src; break;
case 8: value = *(int64_t*)src; break;
default: return false;
}
return pb_encode_svarint(stream, value);
}
bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
UNUSED(field);
return pb_encode_fixed64(stream, src);
}
bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
UNUSED(field);
return pb_encode_fixed32(stream, src);
}
bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
pb_bytes_array_t *bytes = (pb_bytes_array_t*)src;
UNUSED(field);
return pb_encode_string(stream, bytes->bytes, bytes->size);
}
bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
UNUSED(field);
return pb_encode_string(stream, (uint8_t*)src, strlen((char*)src));
}
bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
if (field->ptr == NULL)
return false;
return pb_encode_submessage(stream, (pb_field_t*)field->ptr, src);
}

View File

@@ -48,25 +48,56 @@ bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_
* You may want to use these from your caller or callbacks. * You may want to use these from your caller or callbacks.
*/ */
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); /* Encode field header based on LTYPE and field number defined in the field structure.
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number); * Call this from the callback before writing out field contents. */
/* Encode tag based on LTYPE and field number defined in the field structure. */
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field); bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
/* Write length as varint and then the contents of buffer. */
/* Encode field header by manually specifing wire type. You need to use this if
* you want to write out packed arrays from a callback field. */
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number);
/* Encode an integer in the varint format.
* This works for bool, enum, int32, int64, uint32 and uint64 field types. */
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
/* Encode an integer in the zig-zagged svarint format.
* This works for sint32 and sint64. */
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size); bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size);
/* --- Field encoders --- /* Encode a fixed32, sfixed32 or float value.
* Each encoder writes the content for the field. * You need to pass a pointer to a 4-byte wide C variable. */
* The tag/wire type has been written already. bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
/* Encode a fixed64, sfixed64 or double value.
* You need to pass a pointer to a 8-byte wide C variable. */
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
/* Encode a submessage field.
* You need to pass the pb_field_t array and pointer to struct, just like with pb_encode().
* This internally encodes the submessage twice, first to calculate message size and then to actually write it out.
*/
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
/* --- 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_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
#endif
/* This function is not recommended for new programs. Use pb_encode_submessage()
* instead, it has the same functionality with a less confusing interface. */
bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src); bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
#endif #endif

View File

@@ -1,7 +1,7 @@
CFLAGS=-ansi -Wall -Werror -I .. -g -O0 --coverage CFLAGS=-ansi -Wall -Werror -I .. -g -O0 --coverage
LDFLAGS=--coverage LDFLAGS=--coverage
DEPS=../pb_decode.h ../pb_encode.h ../pb.h person.pb.h callbacks.pb.h unittests.h unittestproto.pb.h alltypes.pb.h DEPS=../pb_decode.h ../pb_encode.h ../pb.h person.pb.h callbacks.pb.h unittests.h unittestproto.pb.h alltypes.pb.h missing_fields.pb.h
TESTS=test_decode1 test_encode1 decode_unittests encode_unittests TESTS=test_decode1 test_encode1 decode_unittests encode_unittests test_no_messages
all: breakpoints $(TESTS) run_unittests all: breakpoints $(TESTS) run_unittests
@@ -25,8 +25,10 @@ test_encode2: test_encode2.o pb_encode.o person.pb.o
test_encode3: test_encode3.o pb_encode.o alltypes.pb.o test_encode3: test_encode3.o pb_encode.o alltypes.pb.o
test_decode_callbacks: test_decode_callbacks.o pb_decode.o callbacks.pb.o test_decode_callbacks: test_decode_callbacks.o pb_decode.o callbacks.pb.o
test_encode_callbacks: test_encode_callbacks.o pb_encode.o callbacks.pb.o test_encode_callbacks: test_encode_callbacks.o pb_encode.o callbacks.pb.o
test_missing_fields: test_missing_fields.o pb_encode.o pb_decode.o missing_fields.pb.o
decode_unittests: decode_unittests.o pb_decode.o unittestproto.pb.o decode_unittests: decode_unittests.o pb_decode.o unittestproto.pb.o
encode_unittests: encode_unittests.o pb_encode.o unittestproto.pb.o encode_unittests: encode_unittests.o pb_encode.o unittestproto.pb.o
test_no_messages: no_messages.pb.h no_messages.pb.c no_messages.pb.o
%.pb: %.proto %.pb: %.proto
protoc -I. -I../generator -I/usr/include -o$@ $< protoc -I. -I../generator -I/usr/include -o$@ $<
@@ -41,7 +43,7 @@ coverage: run_unittests
gcov pb_encode.gcda gcov pb_encode.gcda
gcov pb_decode.gcda gcov pb_decode.gcda
run_unittests: decode_unittests encode_unittests test_encode1 test_encode2 test_encode3 test_decode1 test_decode2 test_decode3 test_encode_callbacks test_decode_callbacks run_unittests: decode_unittests encode_unittests test_encode1 test_encode2 test_encode3 test_decode1 test_decode2 test_decode3 test_encode_callbacks test_decode_callbacks test_missing_fields
rm -f *.gcda rm -f *.gcda
./decode_unittests > /dev/null ./decode_unittests > /dev/null
@@ -62,5 +64,7 @@ run_unittests: decode_unittests encode_unittests test_encode1 test_encode2 test_
./test_encode3 | ./test_decode3 ./test_encode3 | ./test_decode3
./test_encode3 | protoc --decode=AllTypes -I. -I../generator -I/usr/include alltypes.proto >/dev/null ./test_encode3 | protoc --decode=AllTypes -I. -I../generator -I/usr/include alltypes.proto >/dev/null
./test_missing_fields
run_fuzztest: test_decode2 run_fuzztest: test_decode2
bash -c 'I=1; while true; do cat /dev/urandom | ./test_decode2 > /dev/null; I=$$(($$I+1)); echo -en "\r$$I"; done' bash -c 'I=1; while true; do cat /dev/urandom | ./test_decode2 > /dev/null; I=$$(($$I+1)); echo -en "\r$$I"; done'

View File

@@ -1,3 +1,5 @@
#define NANOPB_INTERNALS
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "pb_encode.h" #include "pb_encode.h"
@@ -123,7 +125,6 @@ int main()
uint8_t buffer[30]; uint8_t buffer[30];
pb_ostream_t s; pb_ostream_t s;
uint8_t value = 1; uint8_t value = 1;
int8_t svalue = -1;
int32_t max = INT32_MAX; int32_t max = INT32_MAX;
int32_t min = INT32_MIN; int32_t min = INT32_MIN;
int64_t lmax = INT64_MAX; int64_t lmax = INT64_MAX;
@@ -132,8 +133,6 @@ int main()
COMMENT("Test pb_enc_varint and pb_enc_svarint") COMMENT("Test pb_enc_varint and pb_enc_svarint")
TEST(WRITES(pb_enc_varint(&s, &field, &value), "\x01")); TEST(WRITES(pb_enc_varint(&s, &field, &value), "\x01"));
TEST(WRITES(pb_enc_svarint(&s, &field, &svalue), "\x01"));
TEST(WRITES(pb_enc_svarint(&s, &field, &value), "\x02"));
field.data_size = sizeof(max); field.data_size = sizeof(max);
TEST(WRITES(pb_enc_svarint(&s, &field, &max), "\xfe\xff\xff\xff\x0f")); TEST(WRITES(pb_enc_svarint(&s, &field, &max), "\xfe\xff\xff\xff\x0f"));

138
tests/missing_fields.proto Normal file
View File

@@ -0,0 +1,138 @@
/* Test for one missing field among many */
message AllFields
{
required int32 field1 = 1;
required int32 field2 = 2;
required int32 field3 = 3;
required int32 field4 = 4;
required int32 field5 = 5;
required int32 field6 = 6;
required int32 field7 = 7;
required int32 field8 = 8;
required int32 field9 = 9;
required int32 field10 = 10;
required int32 field11 = 11;
required int32 field12 = 12;
required int32 field13 = 13;
required int32 field14 = 14;
required int32 field15 = 15;
required int32 field16 = 16;
required int32 field17 = 17;
required int32 field18 = 18;
required int32 field19 = 19;
required int32 field20 = 20;
required int32 field21 = 21;
required int32 field22 = 22;
required int32 field23 = 23;
required int32 field24 = 24;
required int32 field25 = 25;
required int32 field26 = 26;
required int32 field27 = 27;
required int32 field28 = 28;
required int32 field29 = 29;
required int32 field30 = 30;
required int32 field31 = 31;
required int32 field32 = 32;
required int32 field33 = 33;
required int32 field34 = 34;
required int32 field35 = 35;
required int32 field36 = 36;
required int32 field37 = 37;
required int32 field38 = 38;
required int32 field39 = 39;
required int32 field40 = 40;
required int32 field41 = 41;
required int32 field42 = 42;
required int32 field43 = 43;
required int32 field44 = 44;
required int32 field45 = 45;
required int32 field46 = 46;
required int32 field47 = 47;
required int32 field48 = 48;
required int32 field49 = 49;
required int32 field50 = 50;
required int32 field51 = 51;
required int32 field52 = 52;
required int32 field53 = 53;
required int32 field54 = 54;
required int32 field55 = 55;
required int32 field56 = 56;
required int32 field57 = 57;
required int32 field58 = 58;
required int32 field59 = 59;
required int32 field60 = 60;
required int32 field61 = 61;
required int32 field62 = 62;
required int32 field63 = 63;
required int32 field64 = 64;
}
message MissingField
{
required int32 field1 = 1;
required int32 field2 = 2;
required int32 field3 = 3;
required int32 field4 = 4;
required int32 field5 = 5;
required int32 field6 = 6;
required int32 field7 = 7;
required int32 field8 = 8;
required int32 field9 = 9;
required int32 field10 = 10;
required int32 field11 = 11;
required int32 field12 = 12;
required int32 field13 = 13;
required int32 field14 = 14;
required int32 field15 = 15;
required int32 field16 = 16;
required int32 field17 = 17;
required int32 field18 = 18;
required int32 field19 = 19;
required int32 field20 = 20;
required int32 field21 = 21;
required int32 field22 = 22;
required int32 field23 = 23;
required int32 field24 = 24;
required int32 field25 = 25;
required int32 field26 = 26;
required int32 field27 = 27;
required int32 field28 = 28;
required int32 field29 = 29;
required int32 field30 = 30;
required int32 field31 = 31;
required int32 field32 = 32;
required int32 field33 = 33;
required int32 field34 = 34;
required int32 field35 = 35;
required int32 field36 = 36;
required int32 field37 = 37;
required int32 field38 = 38;
required int32 field39 = 39;
required int32 field40 = 40;
required int32 field41 = 41;
required int32 field42 = 42;
required int32 field43 = 43;
required int32 field44 = 44;
required int32 field45 = 45;
required int32 field46 = 46;
required int32 field47 = 47;
required int32 field48 = 48;
required int32 field49 = 49;
required int32 field50 = 50;
required int32 field51 = 51;
required int32 field52 = 52;
required int32 field53 = 53;
required int32 field54 = 54;
required int32 field55 = 55;
required int32 field56 = 56;
required int32 field57 = 57;
required int32 field58 = 58;
required int32 field59 = 59;
required int32 field60 = 60;
required int32 field61 = 61;
required int32 field62 = 62;
/* required int32 field63 = 63; */
required int32 field64 = 64;
}

7
tests/no_messages.proto Normal file
View File

@@ -0,0 +1,7 @@
/* Test that a file without any messages works. */
enum Test {
First = 1;
}

View File

@@ -29,7 +29,7 @@ bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *a
return false; return false;
uint32_t value = 42; uint32_t value = 42;
return pb_enc_fixed32(stream, field, &value); return pb_encode_fixed32(stream, &value);
} }
bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *arg) bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
@@ -38,7 +38,7 @@ bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *a
return false; return false;
uint64_t value = 42; uint64_t value = 42;
return pb_enc_fixed64(stream, field, &value); return pb_encode_fixed64(stream, &value);
} }
int main() int main()

View File

@@ -0,0 +1,49 @@
/* Checks that missing required fields are detected properly */
#include <stdio.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "missing_fields.pb.h"
int main()
{
uint8_t buffer[512] = {};
/* Create a message with one missing field */
{
MissingField msg = {};
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&stream, MissingField_fields, &msg))
{
printf("Encode failed.\n");
return 1;
}
}
/* Test that it decodes properly if we don't require that field */
{
MissingField msg = {};
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer));
if (!pb_decode(&stream, MissingField_fields, &msg))
{
printf("Decode failed.\n");
return 2;
}
}
/* Test that it does *not* decode properly if we require the field */
{
AllFields msg = {};
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer));
if (pb_decode(&stream, AllFields_fields, &msg))
{
printf("Decode didn't detect missing field.\n");
return 3;
}
}
return 0; /* All ok */
}