Compare commits

...

12 Commits

Author SHA1 Message Date
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
Petteri Aimonen
0cdc623050 Modified nanopb_generator.py to generate includes for other .proto files.
Implementation was suggested by extremeblue99.
Fixes issue 4.
2012-02-15 17:34:48 +02:00
Petteri Aimonen
f6b08404fa Fixed nanopb_generator.py to read the input file in binary mode. 2012-01-30 10:36:17 +02:00
Petteri Aimonen
b36a1a259a Improved documentation on field decoders. 2012-01-23 18:13:26 +02:00
Petteri Aimonen
113bd7ee87 Fixed issue 1 reported by Erik Rosen:
The size of non-callback bytes-fields was miscalculated, which
caused all following fields in a message to contain garbage.

Previous commit contains a testcase for this.

This fix changes the generated message description. If your protocol uses
bytes-fields, you should regenerate *.pb.c.
2012-01-12 19:08:05 +02:00
Petteri Aimonen
0f6b615ae3 Added an encode/decode test for 'required' fields of all types. 2012-01-12 19:06:33 +02:00
Petteri Aimonen
a1adf39805 Fixed a bug in the generator that caused a compiler error on sfixed32 and sfixed64 fields. 2012-01-12 18:10:12 +02:00
17 changed files with 463 additions and 221 deletions

View File

@@ -160,6 +160,9 @@ required bytes data = 1 [(nanopb).max_size = 40];
The maximum lengths are checked in runtime. If string/bytes/array exceeds the allocated length, *pb_decode* will return false. The maximum lengths are checked in runtime. If string/bytes/array exceeds the allocated length, *pb_decode* will return false.
Note: for the *bytes* datatype, the field length checking may not be exact.
The compiler may add some padding to the *pb_bytes_t* structure, and the nanopb runtime doesn't know how much of the structure size is padding. Therefore it uses the whole length of the structure for storing data, which is not very smart but shouldn't cause problems. In practise, this means that if you specify *(nanopb).max_size=5* on a *bytes* field, you may be able to store 6 bytes there. For the *string* field type, the length limit is exact.
Field callbacks Field callbacks
=============== ===============
When a field has dynamic length, nanopb cannot statically allocate storage for it. Instead, it allows you to handle the field in whatever way you want, using a callback function. When a field has dynamic length, nanopb cannot statically allocate storage for it. Instead, it allows you to handle the field in whatever way you want, using a callback function.

View File

@@ -149,17 +149,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 +165,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 +191,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.
: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. :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. :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
=========== ===========
@@ -376,7 +336,12 @@ Because of memory concerns, the detection of missing required fields is not perf
Each field decoder reads and decodes a single value. For arrays, the decoder is called repeatedly. Each field decoder reads and decodes a single value. For arrays, the decoder is called repeatedly.
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 most of the 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*. 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.
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.
pb_dec_varint pb_dec_varint
------------- -------------
@@ -403,12 +368,12 @@ pb_dec_fixed32
-------------- --------------
Field decoder for PB_LTYPE_FIXED32. :: Field decoder for PB_LTYPE_FIXED32. ::
bool pb_dec_fixed(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);
:stream: Input stream to read from. 1-10 bytes will be read. :stream: Input stream to read from. 4 bytes will be read.
:field: Not used. :field: Not used.
:dest: Pointer to destination integer. Must have size of *field->data_size* bytes. :dest: Pointer to destination *int32_t*, *uint32_t* or *float*.
:returns: True on success, false on IO errors or if `pb_decode_varint`_ fails. :returns: True on success, false on IO errors.
This function reads 4 bytes from the input stream. 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.
@@ -420,6 +385,11 @@ Field decoder for PB_LTYPE_FIXED64. ::
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);
:stream: Input stream to read from. 8 bytes will be read.
:field: Not used.
: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. Same as `pb_dec_fixed32`_, except this reads 8 bytes.
pb_dec_bytes pb_dec_bytes
@@ -428,6 +398,9 @@ 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); bool pb_dec_bytes(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 bytes fields in callbacks, use
*stream->bytes_left* and `pb_read`_.
:stream: Input stream to read from. :stream: Input stream to read from.
:field: Field description structure. Only *field->data_size* matters. :field: Field description structure. Only *field->data_size* matters.
:dest: Pointer to a structure similar to pb_bytes_array_t. :dest: Pointer to a structure similar to pb_bytes_array_t.
@@ -441,6 +414,9 @@ 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); 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. :stream: Input stream to read from.
:field: Field description structure. Only *field->data_size* matters. :field: Field description structure. Only *field->data_size* matters.
:dest: Pointer to a character array of size *field->data_size*. :dest: Pointer to a character array of size *field->data_size*.
@@ -454,6 +430,9 @@ Field decoder for PB_LTYPE_SUBMESSAGE. Calls `pb_decode`_ to perform the actual
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)
**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. :stream: Input stream to read from.
:field: Field description structure. Only *field->ptr* matters. :field: Field description structure. Only *field->ptr* matters.
:dest: Pointer to the destination structure. :dest: Pointer to the destination structure.

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;
} }

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)
@@ -14,8 +34,8 @@ datatypes = {
FieldD.TYPE_FLOAT: ('float', 'PB_LTYPE_FIXED32'), FieldD.TYPE_FLOAT: ('float', 'PB_LTYPE_FIXED32'),
FieldD.TYPE_INT32: ('int32_t', 'PB_LTYPE_VARINT'), FieldD.TYPE_INT32: ('int32_t', 'PB_LTYPE_VARINT'),
FieldD.TYPE_INT64: ('int64_t', 'PB_LTYPE_VARINT'), FieldD.TYPE_INT64: ('int64_t', 'PB_LTYPE_VARINT'),
FieldD.TYPE_SFIXED32: ('int32_t', 'PB_LTYPE_FIXED'), FieldD.TYPE_SFIXED32: ('int32_t', 'PB_LTYPE_FIXED32'),
FieldD.TYPE_SFIXED64: ('int64_t', 'PB_LTYPE_FIXED'), FieldD.TYPE_SFIXED64: ('int64_t', 'PB_LTYPE_FIXED64'),
FieldD.TYPE_SINT32: ('int32_t', 'PB_LTYPE_SVARINT'), FieldD.TYPE_SINT32: ('int32_t', 'PB_LTYPE_SVARINT'),
FieldD.TYPE_SINT64: ('int64_t', 'PB_LTYPE_SVARINT'), FieldD.TYPE_SINT64: ('int64_t', 'PB_LTYPE_SVARINT'),
FieldD.TYPE_UINT32: ('uint32_t', 'PB_LTYPE_VARINT'), FieldD.TYPE_UINT32: ('uint32_t', 'PB_LTYPE_VARINT'),
@@ -219,9 +239,6 @@ class Field:
result += '\n pb_membersize(%s, %s[0]),' % (self.struct_name, self.name) result += '\n pb_membersize(%s, %s[0]),' % (self.struct_name, self.name)
result += ('\n pb_membersize(%s, %s) / pb_membersize(%s, %s[0]),' result += ('\n pb_membersize(%s, %s) / pb_membersize(%s, %s[0]),'
% (self.struct_name, self.name, self.struct_name, self.name)) % (self.struct_name, self.name, self.struct_name, self.name))
elif self.htype != 'PB_HTYPE_CALLBACK' and self.ltype == 'PB_LTYPE_BYTES':
result += '\n pb_membersize(%s, bytes),' % self.ctype
result += ' 0,'
else: else:
result += '\n pb_membersize(%s, %s),' % (self.struct_name, self.name) result += '\n pb_membersize(%s, %s),' % (self.struct_name, self.name)
result += ' 0,' result += ' 0,'
@@ -350,7 +367,7 @@ def sort_dependencies(messages):
if msgname in message_by_name: if msgname in message_by_name:
yield message_by_name[msgname] yield message_by_name[msgname]
def generate_header(headername, enums, messages): def generate_header(dependencies, headername, enums, messages):
'''Generate content for a header file. '''Generate content for a header file.
Generates strings, which should be concatenated and stored to file. Generates strings, which should be concatenated and stored to file.
''' '''
@@ -362,6 +379,11 @@ def generate_header(headername, enums, messages):
yield '#define _PB_%s_\n' % symbol yield '#define _PB_%s_\n' % symbol
yield '#include <pb.h>\n\n' yield '#include <pb.h>\n\n'
for dependency in dependencies:
noext = os.path.splitext(dependency)[0]
yield '#include "%s.pb.h"\n' % noext
yield '\n'
yield '/* Enum definitions */\n' yield '/* Enum definitions */\n'
for enum in enums: for enum in enums:
yield str(enum) + '\n\n' yield str(enum) + '\n\n'
@@ -407,7 +429,7 @@ if __name__ == '__main__':
print "Output fill be written to file.pb.h and file.pb.c" print "Output fill be written to file.pb.h and file.pb.c"
sys.exit(1) sys.exit(1)
data = open(sys.argv[1]).read() data = open(sys.argv[1], 'rb').read()
fdesc = descriptor.FileDescriptorSet.FromString(data) fdesc = descriptor.FileDescriptorSet.FromString(data)
enums, messages = parse_file(fdesc.file[0]) enums, messages = parse_file(fdesc.file[0])
@@ -418,8 +440,13 @@ if __name__ == '__main__':
print "Writing to " + headername + " and " + sourcename print "Writing to " + headername + " and " + sourcename
# List of .proto files that should not be included in the C header file
# even if they are mentioned in the source .proto.
excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto']
dependencies = [d for d in fdesc.file[0].dependency if d not in excludes]
header = open(headername, 'w') header = open(headername, 'w')
for part in generate_header(headerbasename, enums, messages): for part in generate_header(dependencies, headerbasename, enums, messages):
header.write(part) header.write(part)
source = open(sourcename, 'w') source = open(sourcename, 'w')

5
pb.h
View File

@@ -17,6 +17,11 @@
#define pb_packed #define pb_packed
#endif #endif
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#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.

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;
} }
@@ -448,6 +448,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
} }
@@ -480,6 +481,7 @@ bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, v
} }
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
} }
@@ -496,6 +498,7 @@ bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, v
} }
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
} }
@@ -509,7 +512,8 @@ bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, voi
return false; return false;
x->size = temp; x->size = temp;
if (x->size > field->data_size) /* Check length, noting the space taken by the size_t header. */
if (x->size > field->data_size - offsetof(pb_bytes_array_t, bytes))
return false; return false;
return pb_read(stream, x->bytes, x->size); return pb_read(stream, x->bytes, x->size);
@@ -522,7 +526,8 @@ bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, vo
if (!pb_decode_varint32(stream, &size)) if (!pb_decode_varint32(stream, &size))
return false; return false;
if (size > field->data_size - 1) /* Check length, noting the null terminator */
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

@@ -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;
@@ -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,6 +221,40 @@ 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__
uint8_t *bytes = value;
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*)value, 4);
#endif
}
bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
{
#ifdef __BIG_ENDIAN__
uint8_t *bytes[8] = value;
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*)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);
@@ -265,94 +300,14 @@ bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, s
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;
@@ -373,7 +328,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 +339,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,12 +1,12 @@
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 DEPS=../pb_decode.h ../pb_encode.h ../pb.h person.pb.h callbacks.pb.h unittests.h unittestproto.pb.h alltypes.pb.h
TESTS=test_decode1 test_encode1 decode_unittests encode_unittests TESTS=test_decode1 test_encode1 decode_unittests encode_unittests
all: breakpoints $(TESTS) run_unittests all: breakpoints $(TESTS) run_unittests
clean: clean:
rm -f $(TESTS) person.pb* *.o *.gcda *.gcno rm -f $(TESTS) person.pb* alltypes.pb* *.o *.gcda *.gcno
%.o: %.c %.o: %.c
%.o: %.c $(DEPS) %.o: %.c $(DEPS)
@@ -19,8 +19,10 @@ pb_decode.o: ../pb_decode.c $(DEPS)
test_decode1: test_decode1.o pb_decode.o person.pb.o test_decode1: test_decode1.o pb_decode.o person.pb.o
test_decode2: test_decode2.o pb_decode.o person.pb.o test_decode2: test_decode2.o pb_decode.o person.pb.o
test_decode3: test_decode3.o pb_decode.o alltypes.pb.o
test_encode1: test_encode1.o pb_encode.o person.pb.o test_encode1: test_encode1.o pb_encode.o person.pb.o
test_encode2: test_encode2.o pb_encode.o person.pb.o test_encode2: test_encode2.o pb_encode.o person.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
decode_unittests: decode_unittests.o pb_decode.o unittestproto.pb.o decode_unittests: decode_unittests.o pb_decode.o unittestproto.pb.o
@@ -39,7 +41,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_decode1 test_decode2 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
rm -f *.gcda rm -f *.gcda
./decode_unittests > /dev/null ./decode_unittests > /dev/null
@@ -57,5 +59,8 @@ run_unittests: decode_unittests encode_unittests test_encode1 test_encode2 test_
[ "`./test_encode_callbacks | ./test_decode_callbacks`" = \ [ "`./test_encode_callbacks | ./test_decode_callbacks`" = \
"`./test_encode_callbacks | protoc --decode=TestMessage callbacks.proto`" ] "`./test_encode_callbacks | protoc --decode=TestMessage callbacks.proto`" ]
./test_encode3 | ./test_decode3
./test_encode3 | protoc --decode=AllTypes -I. -I../generator -I/usr/include alltypes.proto >/dev/null
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'

40
tests/alltypes.proto Normal file
View File

@@ -0,0 +1,40 @@
import "nanopb.proto";
message SubMessage {
required string substuff1 = 1 [(nanopb).max_size = 16];
required int32 substuff2 = 2;
}
enum MyEnum {
First = 1;
Second = 2;
Truth = 42;
}
message AllTypes {
required int32 req_int32 = 1;
required int64 req_int64 = 2;
required uint32 req_uint32 = 3;
required uint64 req_uint64 = 4;
required sint32 req_sint32 = 5;
required sint64 req_sint64 = 6;
required bool req_bool = 7;
required fixed32 req_fixed32 = 8;
required sfixed32 req_sfixed32= 9;
required float req_float = 10;
required fixed64 req_fixed64 = 11;
required sfixed64 req_sfixed64= 12;
required double req_double = 13;
required string req_string = 14 [(nanopb).max_size = 16];
required bytes req_bytes = 15 [(nanopb).max_size = 16];
required SubMessage req_submsg = 16;
required MyEnum req_enum = 17;
// Just to make sure that the size of the fields has been calculated
// properly, i.e. otherwise a bug in last field might not be detected.
required int32 end = 99;
}

View File

@@ -167,14 +167,22 @@ int main()
{ {
pb_istream_t s; pb_istream_t s;
struct { size_t size; uint8_t bytes[5]; } d; struct { size_t size; uint8_t bytes[5]; } d;
pb_field_t f = {1, PB_LTYPE_BYTES, 0, 0, 5, 0, 0}; pb_field_t f = {1, PB_LTYPE_BYTES, 0, 0, sizeof(d), 0, 0};
COMMENT("Test pb_dec_bytes") COMMENT("Test pb_dec_bytes")
TEST((s = S("\x00"), pb_dec_bytes(&s, &f, &d) && d.size == 0)) TEST((s = S("\x00"), pb_dec_bytes(&s, &f, &d) && d.size == 0))
TEST((s = S("\x01\xFF"), pb_dec_bytes(&s, &f, &d) && d.size == 1 && d.bytes[0] == 0xFF)) TEST((s = S("\x01\xFF"), pb_dec_bytes(&s, &f, &d) && d.size == 1 && d.bytes[0] == 0xFF))
TEST((s = S("\x06xxxxxx"), !pb_dec_bytes(&s, &f, &d)))
TEST((s = S("\x05xxxxx"), pb_dec_bytes(&s, &f, &d) && d.size == 5)) TEST((s = S("\x05xxxxx"), pb_dec_bytes(&s, &f, &d) && d.size == 5))
TEST((s = S("\x05xxxx"), !pb_dec_bytes(&s, &f, &d))) TEST((s = S("\x05xxxx"), !pb_dec_bytes(&s, &f, &d)))
/* Note: the size limit on bytes-fields is not strictly obeyed, as
* the compiler may add some padding to the struct. Using this padding
* is not a very good thing to do, but it is difficult to avoid when
* we use only a single uint8_t to store the size of the field.
* Therefore this tests against a 10-byte string, while otherwise even
* 6 bytes should error out.
*/
TEST((s = S("\x10xxxxxxxxxx"), !pb_dec_bytes(&s, &f, &d)))
} }
{ {

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"));

70
tests/test_decode3.c Normal file
View File

@@ -0,0 +1,70 @@
/* Tests the decoding of all types. Currently only in the 'required' variety.
* This is the counterpart of test_encode3.
* Run e.g. ./test_encode3 | ./test_decode3
*/
#include <stdio.h>
#include <string.h>
#include <pb_decode.h>
#include "alltypes.pb.h"
#define TEST(x) if (!(x)) { \
printf("Test " #x " failed.\n"); \
return false; \
}
/* This function is called once from main(), it handles
the decoding and checks the fields. */
bool check_alltypes(pb_istream_t *stream)
{
AllTypes alltypes = {};
if (!pb_decode(stream, AllTypes_fields, &alltypes))
return false;
TEST(alltypes.req_int32 == 1001);
TEST(alltypes.req_int64 == 1002);
TEST(alltypes.req_uint32 == 1003);
TEST(alltypes.req_uint64 == 1004);
TEST(alltypes.req_sint32 == 1005);
TEST(alltypes.req_sint64 == 1006);
TEST(alltypes.req_bool == true);
TEST(alltypes.req_fixed32 == 1008);
TEST(alltypes.req_sfixed32 == 1009);
TEST(alltypes.req_float == 1010.0f);
TEST(alltypes.req_fixed64 == 1011);
TEST(alltypes.req_sfixed64 == 1012);
TEST(alltypes.req_double == 1013.0f);
TEST(strcmp(alltypes.req_string, "1014") == 0);
TEST(alltypes.req_bytes.size == 4);
TEST(memcmp(alltypes.req_bytes.bytes, "1015", 4) == 0);
TEST(strcmp(alltypes.req_submsg.substuff1, "1016") == 0);
TEST(alltypes.req_submsg.substuff2 == 1016);
TEST(alltypes.req_enum == MyEnum_Truth);
TEST(alltypes.end == 1099);
return true;
}
int main()
{
/* Read the data into buffer */
uint8_t buffer[512];
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
/* Construct a pb_istream_t for reading from the buffer */
pb_istream_t stream = pb_istream_from_buffer(buffer, count);
/* Decode and print out the stuff */
if (!check_alltypes(&stream))
{
printf("Parsing failed.\n");
return 1;
} else {
return 0;
}
}

50
tests/test_encode3.c Normal file
View File

@@ -0,0 +1,50 @@
/* Attempts to test all the datatypes supported by ProtoBuf.
* Currently only tests the 'required' variety.
*/
#include <stdio.h>
#include <pb_encode.h>
#include "alltypes.pb.h"
int main()
{
/* Initialize the structure with constants */
AllTypes alltypes = {
1001,
1002,
1003,
1004,
1005,
1006,
true,
1008,
1009,
1010.0f,
1011,
1012,
1013.0,
"1014",
{4, "1015"},
{"1016", 1016},
MyEnum_Truth,
1099
};
uint8_t buffer[512];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
/* Now encode it and check if we succeeded. */
if (pb_encode(&stream, AllTypes_fields, &alltypes))
{
fwrite(buffer, 1, stream.bytes_written, stdout);
return 0; /* Success */
}
else
{
return 1; /* Failure */
}
}

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()