Compare commits

...

40 Commits

Author SHA1 Message Date
Petteri Aimonen
80a2d33fa9 Move STATIC_ASSERTs to .pb.c file.
This way the .pb.h will remain cleaner and easier to read.
2013-02-21 19:35:20 +02:00
Petteri Aimonen
41f98343c8 Separate PB_HTYPE to PB_ATYPE and PB_HTYPE.
Also clean up the logic so that it is easier to implement more
allocation types in the future.

Update issue 53
Status: FixedInGit
2013-02-20 22:55:59 +02:00
Petteri Aimonen
69085d9387 Rename PB_HTYPE_ARRAY -> PB_HTYPE_REPEATED.
This is a more logical name in parallel with PB_HTYPE_REQUIRED and PB_HTYPE_OPTIONAL.

Warning: This breaks backwards-compatibility of generated .pb.c files.
You will have to regenerate the files and recompile.
2013-02-20 21:58:18 +02:00
Petteri Aimonen
258ba8335d Reformat generated .pb.c files using macros.
This has the following advantages:
1) Easier to modify pb_field_t encoding
2) Simpler generator logic
3) Tidier looking, easier to read .pb.c files

Update issue 58
Status: FixedInGit
2013-02-17 00:10:47 +02:00
Petteri Aimonen
9d3d7b5730 Add gitignore.
This is a bit bloated gitignore file. Having binaries
in a separate build directory would be cleaner, but I don't
bother to make that change now.
2013-02-11 22:03:14 +02:00
Petteri Aimonen
ec4a7a0cce Replace pb_type_t enum with #defines.
See issue #57.
2013-02-11 21:55:55 +02:00
Petteri Aimonen
c1a355b23e Set version to 0.2.0-dev 2013-02-11 21:54:24 +02:00
Petteri Aimonen
22e0a78e5e Update changelog 2013-02-07 18:06:42 +02:00
Petteri Aimonen
c1bd1a6ad3 Fix error message bugs with packed arrays.
Error messages were not propagated correctly with PB_HTYPE_ARRAY.
Error status (boolean return value) was correct.

Update issue 56
Status: FixedInGit
2013-02-07 17:56:52 +02:00
Petteri Aimonen
e7bf063abc Add check for sizeof(double) == 8.
Update issue 54
Status: FixedInGit
2013-02-07 17:48:50 +02:00
Petteri Aimonen
47b10ec0ab Sanitize filenames before putting them in #ifndef.
Update issue 50
Status: FixedInGit
2013-02-07 17:40:17 +02:00
Petteri Aimonen
4b7ddabbcf Fix compiler warning on MSVC (issue #57) 2013-02-07 17:19:53 +02:00
Petteri Aimonen
c3729599b0 Use unsigned datatypes where appropriate. 2013-02-06 22:18:52 +02:00
Petteri Aimonen
4f379364b3 Improve the pb_decode_varint implementations.
Results for ARM: -4% execution time, +1% code size
2013-02-06 22:11:02 +02:00
Petteri Aimonen
d23939d688 Avoid unnecessary looping in required fields check.
Results for ARM: -6% execution time, -1% code size
2013-02-06 21:52:59 +02:00
Petteri Aimonen
4ba6a3027d Add compile-time option PB_BUFFER_ONLY.
This allows slight optimizations if only memory buffer support
(as opposed to stream callbacks) is wanted. On ARM difference
is -12% execution time, -4% code size when enabled.
2013-02-06 20:54:25 +02:00
Petteri Aimonen
39b8a5e2bb Make pb_decode_varint32 a separate implementation.
This avoids doing 64-bit arithmetic for 32-bit varint decodings.
It does increase the code size somewhat.

Results for ARM Cortex-M3: -10% execution time, +1% code size, -2% ram usage.
2013-02-05 22:39:32 +02:00
Petteri Aimonen
c372ebc665 Performance improvement: replace memcpy with loop.
In the pb_istream_from_buffer and pb_ostream_from_buffer, memcpy was
used to transfer values to the buffer. For the common case of
count = 1-10 bytes, a simple loop is faster.
2013-02-05 22:06:36 +02:00
Petteri Aimonen
38ced18639 Add Java package name to nanopb.proto.
This fixes problems when interoperating with Java.

Fix suggested by trepidacious:
https://groups.google.com/forum/#!topic/nanopb/hZgrj6h9OnY
2013-02-05 11:47:37 +02:00
Petteri Aimonen
e4b55179d1 Add an example of handling doubles on AVR platform. 2013-01-29 22:10:37 +02:00
Petteri Aimonen
2392d25574 Fix misleading comment (current generator sets LTYPE always). 2013-01-21 21:18:38 +02:00
Petteri Aimonen
eab4151a99 Oops, typoed #ifdef; again fixing previous commit. 2013-01-16 16:32:48 +02:00
Petteri Aimonen
8e840cc81a Put previous commit (issue 52) inside #ifndef 2013-01-16 16:31:45 +02:00
Petteri Aimonen
88eba4bc27 Fix bug with error messages.
Update issue 52
Status: FixedInGit
2013-01-16 16:28:51 +02:00
Petteri Aimonen
d32d322658 Add field type FT_IGNORE to generator.
This allows ignoring fields that are unnecessary or too large for an
embedded system using nanopb, while allowing them to remain in the .proto
for other platforms.

Update issue 51
Status: FixedInGit
2013-01-14 17:30:49 +02:00
Petteri Aimonen
b9baec6b4c Add a test for the backwards compatibility of generated files.
It is not necessary to maintain full compatibility of generated files
for all of eternity, but this test will warn us if there is
a need to regenerate the files.
2013-01-13 18:44:15 +02:00
Petteri Aimonen
d2c1604d6d Add generator option for packed structs.
Usage is:
message Foo
{
   option (nanopb_msgopt).packed_struct = true;
   ...
}

Valid also in file scope.

Update issue 49
Status: FixedInGit
2013-01-10 17:32:27 +02:00
Petteri Aimonen
93ffe14a0a Cleanup: get rid of unnecessary spaces 2013-01-10 17:31:33 +02:00
Petteri Aimonen
bb5dc04584 Setting version to 0.1.9-dev 2012-12-13 21:32:37 +02:00
Petteri Aimonen
09ec60cadf Publishing nanopb-0.1.8 2012-12-13 21:29:57 +02:00
Petteri Aimonen
871e5be9dd Fix small error in field callback documentation.
Update issue 44
Status: FixedInGit
2012-12-06 11:13:53 +02:00
Jens Steinhauser
068de05c51 Complete initialization of pb_istream_t.
Because PB_RETURN_ERROR checks if the 'errmsg' member is NULL before
assigning to it, error messages would get lost.
2012-11-27 19:55:21 +02:00
Steffen Siering
1f8fb1f1ed Use TESTS variable to define dependencies for run_unittests 2012-11-16 09:59:04 +02:00
Petteri Aimonen
434dcbb2ee Select compilation options based on $(CC) in tests/Makefile.
Makes 'make CC=clang' work.

Based on patch submitted by Steffen Siering.

Update issue 40:
Status: FixedInGit
2012-11-16 09:51:23 +02:00
Petteri Aimonen
59788e2aab Rename test_compiles.c to test_multiple_files.c 2012-11-16 09:33:11 +02:00
Steffen Siering
cc29958d34 Fix STATIC_ASSERT macro when using multiple .proto files.
The __COUNTER__ macro (used for generating unique names) is at least supported
by gcc, clang and Visual Studio. With this change test_compiles.c is
compilable, since no more typedefs are redefined.

Compilers/Preprocessors not supporting __COUNTER__ error's are still possible
which are hopfully handled by the usage of __LINE__ in most sittuations.

Added unit test for the problem.
2012-11-16 09:24:39 +02:00
Petteri Aimonen
0abb764b18 Fix naming of nested Enums with short names 2012-11-14 00:43:51 +02:00
Petteri Aimonen
02ecee2de8 Fix naming of enums when long_names=false.
Modify test case to check that options.pb.o compiles.

Update issue 42
Status: FixedInGit

Update issue 43
Status: FixedInGit
2012-11-14 00:36:16 +02:00
Petteri Aimonen
332a9ee95c Fix changelog 2012-11-11 11:20:34 +02:00
Petteri Aimonen
fc6f56b2bd Setting version to 0.1.8-dev 2012-11-11 11:19:17 +02:00
29 changed files with 1719 additions and 351 deletions

39
.gitignore vendored Normal file
View File

@@ -0,0 +1,39 @@
*.gcda
*.gcno
*.gcov
*.o
*.pb.c
*.pb.h
*.pb
*~
*.tar.gz
julkaisu.txt
docs/*.html
docs/generator_flow.png
example/client
example/server
example_avr_double/decode_double
example_avr_double/encode_double
example_avr_double/test_conversions
example_unions/decode
example_unions/encode
generator/nanopb_pb2.pyc
tests/decode_unittests
tests/encode_unittests
tests/test_compiles
tests/test_decode1
tests/test_decode2
tests/test_decode3
tests/test_decode3_buf
tests/test_decode_callbacks
tests/test_encode1
tests/test_encode2
tests/test_encode3
tests/test_encode3_buf
tests/test_encode_callbacks
tests/test_missing_fields
tests/test_multiple_files
tests/bc_decode
tests/bc_encode
tests/breakpoints

View File

@@ -1,4 +1,21 @@
nanopb-0.1.7 (not yet released)
git master
Fixed error message bugs (issues 52, 56)
Sanitize #ifndef filename (issue 50)
Performance improvements
Add compile-time option PB_BUFFER_ONLY
Add Java package name to nanopb.proto
Check for sizeof(double) == 8 (issue 54)
Added generator option to ignore some fields. (issue 51)
Added generator option to make message structs packed. (issue 49)
Add more test cases.
nanopb-0.1.8
Fix bugs in the enum short names introduced in 0.1.7 (issues 42, 43)
Fix STATIC_ASSERT macro when using multiple .proto files. (issue 41)
Fix missing initialization of istream.errmsg
Make tests/Makefile work for non-gcc compilers (issue 40)
nanopb-0.1.7
Remove "skip" mode from pb_istream_t callbacks. Example implementation had a bug. (issue 37)
Add option to use shorter names for enum values (issue 38)
Improve options support in generator (issues 12, 30)

View File

@@ -22,6 +22,8 @@ PB_FIELD_32BIT Add support for tag numbers > 65535 and fields la
Increases code size 9 bytes per each field. Compiler error will tell if you need this.
PB_NO_ERRMSG Disables the support for error messages; only error information is the true/false return value.
Decreases the code size by a few hundred bytes.
PB_BUFFER_ONLY Disables the support for custom streams. Only supports encoding to memory buffers.
Speeds up execution and decreases code size slightly.
============================ ================================================================================================
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow raising some datatype limits to suit larger messages.
@@ -381,7 +383,7 @@ Remove the data for a field from the stream, without actually decoding it::
For decoding numeric (including enumerated and boolean) values, use `pb_decode_varint`_, `pb_decode_svarint`_, `pb_decode_fixed32`_ and `pb_decode_fixed64`_. They take a pointer to a 32- or 64-bit C variable, which you may then cast to smaller datatype for storage.
For decoding strings and bytes fields, the length has already been decoded. You can therefore check the total length in *stream->state* and read the data using `pb_read`_.
For decoding strings and bytes fields, the length has already been decoded. You can therefore check the total length in *stream->bytes_left* and read the data using `pb_read`_.
Finally, for decoding submessages in a callback, simply use `pb_decode`_ and pass it the *SubMessage_fields* descriptor array.

View File

@@ -0,0 +1,22 @@
CFLAGS=-Wall -Werror -I .. -g -O0
DEPS=double_conversion.c ../pb_decode.c ../pb_decode.h ../pb_encode.c ../pb_encode.h ../pb.h
all: run_tests
clean:
rm -f test_conversions encode_double decode_double doubleproto.pb.c doubleproto.pb.h
test_conversions: test_conversions.c double_conversion.c
$(CC) $(CFLAGS) -o $@ $^
%: %.c $(DEPS) doubleproto.pb.h doubleproto.pb.c
$(CC) $(CFLAGS) -o $@ $< double_conversion.c ../pb_decode.c ../pb_encode.c doubleproto.pb.c
doubleproto.pb.c doubleproto.pb.h: doubleproto.proto ../generator/nanopb_generator.py
protoc -I. -I../generator -I/usr/include -odoubleproto.pb $<
python ../generator/nanopb_generator.py doubleproto.pb
run_tests: test_conversions encode_double decode_double
./test_conversions
./encode_double | ./decode_double

View File

@@ -0,0 +1,22 @@
Some processors/compilers, such as AVR-GCC, do not support the double
datatype. Instead, they have sizeof(double) == 4. Because protocol
binary format uses the double encoding directly, this causes trouble
if the protocol in .proto requires double fields.
This directory contains a solution to this problem. It uses uint64_t
to store the raw wire values, because its size is correct on all
platforms. The file double_conversion.c provides functions that
convert these values to/from floats, without relying on compiler
support.
To use this method, you need to make two modifications to your code:
1) Change all 'double' fields into 'fixed64' in the .proto.
2) Whenever writing to a 'double' field, use float_to_double().
3) Whenever reading a 'double' field, use double_to_float().
The conversion routines should be as accurate as the float datatype can
be. Furthermore, they should handle all special values (NaN, inf, denormalized
numbers) correctly. There are testcases in test_conversions.c.

View File

@@ -0,0 +1,33 @@
/* Decodes a double value into a float variable.
* Used to read double values with AVR code, which doesn't support double directly.
*/
#include <stdio.h>
#include <pb_decode.h>
#include "double_conversion.h"
#include "doubleproto.pb.h"
int main()
{
uint8_t buffer[32];
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
pb_istream_t stream = pb_istream_from_buffer(buffer, count);
AVRDoubleMessage message;
pb_decode(&stream, AVRDoubleMessage_fields, &message);
float v1 = double_to_float(message.field1);
float v2 = double_to_float(message.field2);
printf("Values: %f %f\n", v1, v2);
if (v1 == 1234.5678f &&
v2 == 0.00001f)
{
return 0;
}
else
{
return 1;
}
}

View File

@@ -0,0 +1,123 @@
/* Conversion routines for platforms that do not support 'double' directly. */
#include "double_conversion.h"
#include <math.h>
typedef union {
float f;
uint32_t i;
} conversion_t;
/* Note: IEE 754 standard specifies float formats as follows:
* Single precision: sign, 8-bit exp, 23-bit frac.
* Double precision: sign, 11-bit exp, 52-bit frac.
*/
uint64_t float_to_double(float value)
{
conversion_t in;
in.f = value;
uint8_t sign;
int16_t exponent;
uint64_t mantissa;
/* Decompose input value */
sign = (in.i >> 31) & 1;
exponent = ((in.i >> 23) & 0xFF) - 127;
mantissa = in.i & 0x7FFFFF;
if (exponent == 128)
{
/* Special value (NaN etc.) */
exponent = 1024;
}
else if (exponent == -127)
{
if (!mantissa)
{
/* Zero */
exponent = -1023;
}
else
{
/* Denormalized */
mantissa <<= 1;
while (!(mantissa & 0x800000))
{
mantissa <<= 1;
exponent--;
}
mantissa &= 0x7FFFFF;
}
}
/* Combine fields */
mantissa <<= 29;
mantissa |= (uint64_t)(exponent + 1023) << 52;
mantissa |= (uint64_t)sign << 63;
return mantissa;
}
float double_to_float(uint64_t value)
{
uint8_t sign;
int16_t exponent;
uint32_t mantissa;
conversion_t out;
/* Decompose input value */
sign = (value >> 63) & 1;
exponent = ((value >> 52) & 0x7FF) - 1023;
mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */
/* Figure if value is in range representable by floats. */
if (exponent == 1024)
{
/* Special value */
exponent = 128;
}
else if (exponent > 127)
{
/* Too large */
if (sign)
return -INFINITY;
else
return INFINITY;
}
else if (exponent < -150)
{
/* Too small */
if (sign)
return -0.0f;
else
return 0.0f;
}
else if (exponent < -126)
{
/* Denormalized */
mantissa |= 0x1000000;
mantissa >>= (-126 - exponent);
exponent = -127;
}
/* Round off mantissa */
mantissa = (mantissa + 1) >> 1;
/* Check if mantissa went over 2.0 */
if (mantissa & 0x800000)
{
exponent += 1;
mantissa &= 0x7FFFFF;
mantissa >>= 1;
}
/* Combine fields */
out.i = mantissa;
out.i |= (uint32_t)(exponent + 127) << 23;
out.i |= (uint32_t)sign << 31;
return out.f;
}

View File

@@ -0,0 +1,26 @@
/* AVR-GCC does not have real double datatype. Instead its double
* is equal to float, i.e. 32 bit value. If you need to communicate
* with other systems that use double in their .proto files, you
* need to do some conversion.
*
* These functions use bitwise operations to mangle floats into doubles
* and then store them in uint64_t datatype.
*/
#ifndef DOUBLE_CONVERSION
#define DOUBLE_CONVERSION
#include <stdint.h>
/* Convert native 4-byte float into a 8-byte double. */
extern uint64_t float_to_double(float value);
/* Convert 8-byte double into native 4-byte float.
* Values are rounded to nearest, 0.5 away from zero.
* Overflowing values are converted to Inf or -Inf.
*/
extern float double_to_float(uint64_t value);
#endif

View File

@@ -0,0 +1,13 @@
// A message containing doubles, as used by other applications.
message DoubleMessage {
required double field1 = 1;
required double field2 = 2;
}
// A message containing doubles, but redefined using uint64_t.
// For use in AVR code.
message AVRDoubleMessage {
required fixed64 field1 = 1;
required fixed64 field2 = 2;
}

View File

@@ -0,0 +1,25 @@
/* Encodes a float value into a double on the wire.
* Used to emit doubles from AVR code, which doesn't support double directly.
*/
#include <stdio.h>
#include <pb_encode.h>
#include "double_conversion.h"
#include "doubleproto.pb.h"
int main()
{
AVRDoubleMessage message = {
float_to_double(1234.5678f),
float_to_double(0.00001f)
};
uint8_t buffer[32];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
pb_encode(&stream, AVRDoubleMessage_fields, &message);
fwrite(buffer, 1, stream.bytes_written, stdout);
return 0;
}

View File

@@ -0,0 +1,56 @@
#include "double_conversion.h"
#include <math.h>
#include <stdio.h>
static const double testvalues[] = {
0.0, -0.0, 0.1, -0.1,
M_PI, -M_PI, 123456.789, -123456.789,
INFINITY, -INFINITY, NAN, INFINITY - INFINITY,
1e38, -1e38, 1e39, -1e39,
1e-38, -1e-38, 1e-39, -1e-39,
3.14159e-37,-3.14159e-37, 3.14159e-43, -3.14159e-43,
1e-60, -1e-60, 1e-45, -1e-45,
0.99999999999999, -0.99999999999999, 127.999999999999, -127.999999999999
};
#define TESTVALUES_COUNT (sizeof(testvalues)/sizeof(testvalues[0]))
int main()
{
int status = 0;
int i;
for (i = 0; i < TESTVALUES_COUNT; i++)
{
double orig = testvalues[i];
float expected_float = (float)orig;
double expected_double = (double)expected_float;
float got_float = double_to_float(*(uint64_t*)&orig);
uint64_t got_double = float_to_double(got_float);
uint32_t e1 = *(uint32_t*)&expected_float;
uint32_t g1 = *(uint32_t*)&got_float;
uint64_t e2 = *(uint64_t*)&expected_double;
uint64_t g2 = got_double;
if (g1 != e1)
{
printf("%3d double_to_float fail: %08x != %08x\n", i, g1, e1);
status = 1;
}
if (g2 != e2)
{
printf("%3d float_to_double fail: %016llx != %016llx\n", i,
(unsigned long long)g2,
(unsigned long long)e2);
status = 1;
}
}
return status;
}

View File

@@ -7,10 +7,13 @@
import "google/protobuf/descriptor.proto";
option java_package = "fi.kapsi.koti.jpa.nanopb";
enum FieldType {
FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible.
FT_CALLBACK = 1; // Always generate a callback field.
FT_STATIC = 2; // Generate a static field or raise an exception if not possible.
FT_IGNORE = 3; // Ignore the field completely.
}
message NanoPBOptions {
@@ -25,15 +28,18 @@ message NanoPBOptions {
// Use long names for enums, i.e. EnumName_EnumValue.
optional bool long_names = 4 [default = true];
// Add 'packed' attribute to generated structs.
optional bool packed_struct = 5 [default = false];
}
// Protocol Buffers extension number registry
// --------------------------------
// Project: Nanopb
// Contact: Petteri Aimonen <jpa@kapsi.fi>
// Web site: http://kapsi.fi/~jpa/nanopb
// Extensions: 1010 (all types)
// --------------------------------
// --------------------------------
// Project: Nanopb
// Contact: Petteri Aimonen <jpa@kapsi.fi>
// Web site: http://kapsi.fi/~jpa/nanopb
// Extensions: 1010 (all types)
// --------------------------------
extend google.protobuf.FileOptions {
optional NanoPBOptions nanopb_fileopt = 1010;

View File

@@ -1,5 +1,5 @@
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
nanopb_version = "nanopb-0.1.7"
nanopb_version = "0.2.0-dev"
try:
import google.protobuf.descriptor_pb2 as descriptor
@@ -35,22 +35,22 @@ except:
import time
import os.path
# Values are tuple (c type, pb ltype)
# Values are tuple (c type, pb type)
FieldD = descriptor.FieldDescriptorProto
datatypes = {
FieldD.TYPE_BOOL: ('bool', 'PB_LTYPE_VARINT'),
FieldD.TYPE_DOUBLE: ('double', 'PB_LTYPE_FIXED64'),
FieldD.TYPE_FIXED32: ('uint32_t', 'PB_LTYPE_FIXED32'),
FieldD.TYPE_FIXED64: ('uint64_t', 'PB_LTYPE_FIXED64'),
FieldD.TYPE_FLOAT: ('float', 'PB_LTYPE_FIXED32'),
FieldD.TYPE_INT32: ('int32_t', 'PB_LTYPE_VARINT'),
FieldD.TYPE_INT64: ('int64_t', 'PB_LTYPE_VARINT'),
FieldD.TYPE_SFIXED32: ('int32_t', 'PB_LTYPE_FIXED32'),
FieldD.TYPE_SFIXED64: ('int64_t', 'PB_LTYPE_FIXED64'),
FieldD.TYPE_SINT32: ('int32_t', 'PB_LTYPE_SVARINT'),
FieldD.TYPE_SINT64: ('int64_t', 'PB_LTYPE_SVARINT'),
FieldD.TYPE_UINT32: ('uint32_t', 'PB_LTYPE_VARINT'),
FieldD.TYPE_UINT64: ('uint64_t', 'PB_LTYPE_VARINT')
FieldD.TYPE_BOOL: ('bool', 'BOOL'),
FieldD.TYPE_DOUBLE: ('double', 'DOUBLE'),
FieldD.TYPE_FIXED32: ('uint32_t', 'FIXED32'),
FieldD.TYPE_FIXED64: ('uint64_t', 'FIXED64'),
FieldD.TYPE_FLOAT: ('float', 'FLOAT'),
FieldD.TYPE_INT32: ('int32_t', 'INT32'),
FieldD.TYPE_INT64: ('int64_t', 'INT64'),
FieldD.TYPE_SFIXED32: ('int32_t', 'SFIXED32'),
FieldD.TYPE_SFIXED64: ('int64_t', 'SFIXED64'),
FieldD.TYPE_SINT32: ('int32_t', 'SINT32'),
FieldD.TYPE_SINT64: ('int64_t', 'SINT64'),
FieldD.TYPE_UINT32: ('uint32_t', 'UINT32'),
FieldD.TYPE_UINT64: ('uint64_t', 'UINT64')
}
class Names:
@@ -73,6 +73,9 @@ class Names:
else:
raise ValueError("Name parts should be of type str")
def __eq__(self, other):
return isinstance(other, Names) and self.parts == other.parts
def names_from_type_name(type_name):
'''Parse Names() from FieldDescriptorProto type_name'''
if type_name[0] != '.':
@@ -83,12 +86,15 @@ class Enum:
def __init__(self, names, desc, enum_options):
'''desc is EnumDescriptorProto'''
if enum_options.long_names:
self.names = names + desc.name
else:
self.names = names
self.options = enum_options
self.names = names + desc.name
self.values = [(self.names + x.name, x.number) for x in desc.value]
if enum_options.long_names:
self.values = [(self.names + x.name, x.number) for x in desc.value]
else:
self.values = [(names + x.name, x.number) for x in desc.value]
self.value_longnames = [self.names + x.name for x in desc.value]
def __str__(self):
result = 'typedef enum _%s {\n' % self.names
@@ -117,49 +123,44 @@ class Field:
if desc.HasField('default_value'):
self.default = desc.default_value
# Decide HTYPE
# HTYPE is the high-order nibble of nanopb field description,
# defining whether value is required/optional/repeated.
# Check field rules, i.e. required/optional/repeated.
can_be_static = True
if desc.label == FieldD.LABEL_REQUIRED:
self.htype = 'PB_HTYPE_REQUIRED'
self.rules = 'REQUIRED'
elif desc.label == FieldD.LABEL_OPTIONAL:
self.htype = 'PB_HTYPE_OPTIONAL'
self.rules = 'OPTIONAL'
elif desc.label == FieldD.LABEL_REPEATED:
self.rules = 'REPEATED'
if self.max_count is None:
can_be_static = False
else:
self.htype = 'PB_HTYPE_ARRAY'
self.array_decl = '[%d]' % self.max_count
else:
raise NotImplementedError(desc.label)
# Decide LTYPE and CTYPE
# LTYPE is the low-order nibble of nanopb field description,
# defining how to decode an individual value.
# CTYPE is the name of the c type to use in the struct.
# Decide the C data type to use in the struct.
if datatypes.has_key(desc.type):
self.ctype, self.ltype = datatypes[desc.type]
self.ctype, self.pbtype = datatypes[desc.type]
elif desc.type == FieldD.TYPE_ENUM:
self.ltype = 'PB_LTYPE_VARINT'
self.pbtype = 'ENUM'
self.ctype = names_from_type_name(desc.type_name)
if self.default is not None:
self.default = self.ctype + self.default
elif desc.type == FieldD.TYPE_STRING:
self.ltype = 'PB_LTYPE_STRING'
self.pbtype = 'STRING'
if self.max_size is None:
can_be_static = False
else:
self.ctype = 'char'
self.array_decl += '[%d]' % self.max_size
elif desc.type == FieldD.TYPE_BYTES:
self.ltype = 'PB_LTYPE_BYTES'
self.pbtype = 'BYTES'
if self.max_size is None:
can_be_static = False
else:
self.ctype = self.struct_name + self.name + 't'
elif desc.type == FieldD.TYPE_MESSAGE:
self.ltype = 'PB_LTYPE_SUBMESSAGE'
self.pbtype = 'MESSAGE'
self.ctype = self.submsgname = names_from_type_name(desc.type_name)
else:
raise NotImplementedError(desc.type)
@@ -173,18 +174,22 @@ class Field:
if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static:
raise Exception("Field %s is defined as static, but max_size or max_count is not given." % self.name)
if field_options.type == nanopb_pb2.FT_CALLBACK:
self.htype = 'PB_HTYPE_CALLBACK'
if field_options.type == nanopb_pb2.FT_STATIC:
self.allocation = 'STATIC'
elif field_options.type == nanopb_pb2.FT_CALLBACK:
self.allocation = 'CALLBACK'
self.ctype = 'pb_callback_t'
self.array_decl = ''
else:
raise NotImplementedError(field_options.type)
def __cmp__(self, other):
return cmp(self.tag, other.tag)
def __str__(self):
if self.htype == 'PB_HTYPE_OPTIONAL':
if self.rules == 'OPTIONAL':
result = ' bool has_' + self.name + ';\n'
elif self.htype == 'PB_HTYPE_ARRAY':
elif self.rules == 'REPEATED' and self.allocation == 'STATIC':
result = ' size_t ' + self.name + '_count;\n'
else:
result = ''
@@ -193,7 +198,7 @@ class Field:
def types(self):
'''Return definitions for any special types this field might need.'''
if self.ltype == 'PB_LTYPE_BYTES' and self.max_size is not None:
if self.pbtype == 'BYTES' and self.allocation == 'STATIC':
result = 'typedef struct {\n'
result += ' size_t size;\n'
result += ' uint8_t bytes[%d];\n' % self.max_size
@@ -206,30 +211,25 @@ class Field:
'''Return definition for this field's default value.'''
if self.default is None:
return None
ctype, default = self.ctype, self.default
array_decl = ''
if self.ltype == 'PB_LTYPE_STRING':
ctype = 'char'
if self.max_size is None:
if self.pbtype == 'STRING':
if self.allocation != 'STATIC':
return None # Not implemented
else:
array_decl = '[%d]' % (self.max_size + 1)
array_decl = '[%d]' % self.max_size
default = str(self.default).encode('string_escape')
default = default.replace('"', '\\"')
default = '"' + default + '"'
elif self.ltype == 'PB_LTYPE_BYTES':
elif self.pbtype == 'BYTES':
if self.allocation != 'STATIC':
return None # Not implemented
data = self.default.decode('string_escape')
data = ['0x%02x' % ord(c) for c in data]
if self.max_size is None:
return None # Not implemented
else:
ctype = self.ctype
default = '{%d, {%s}}' % (len(data), ','.join(data))
array_decl = ''
else:
ctype, default = self.ctype, self.default
array_decl = ''
if declaration_only:
return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl)
@@ -240,47 +240,30 @@ class Field:
'''Return the pb_field_t initializer to use in the constant array.
prev_field_name is the name of the previous field or None.
'''
result = ' {%d, ' % self.tag
result += '(pb_type_t) ((int) ' + self.htype
if self.ltype is not None:
result += ' | (int) ' + self.ltype
result += '),\n'
result = ' PB_FIELD(%3d, ' % self.tag
result += '%-8s, ' % self.pbtype
result += '%s, ' % self.rules
result += '%s, ' % self.allocation
result += '%s, ' % self.struct_name
result += '%s, ' % self.name
result += '%s, ' % (prev_field_name or self.name)
if prev_field_name is None:
result += ' offsetof(%s, %s),' % (self.struct_name, self.name)
if self.pbtype == 'MESSAGE':
result += '&%s_fields)' % self.submsgname
elif self.default is None:
result += '0)'
elif self.pbtype in ['BYTES', 'STRING'] and self.allocation != 'STATIC':
result += '0)' # Arbitrary size default values not implemented
else:
result += ' pb_delta_end(%s, %s, %s),' % (self.struct_name, self.name, prev_field_name)
if self.htype == 'PB_HTYPE_OPTIONAL':
result += '\n pb_delta(%s, has_%s, %s),' % (self.struct_name, self.name, self.name)
elif self.htype == 'PB_HTYPE_ARRAY':
result += '\n pb_delta(%s, %s_count, %s),' % (self.struct_name, self.name, self.name)
else:
result += ' 0,'
if self.htype == 'PB_HTYPE_ARRAY':
result += '\n pb_membersize(%s, %s[0]),' % (self.struct_name, self.name)
result += ('\n pb_membersize(%s, %s) / pb_membersize(%s, %s[0]),'
% (self.struct_name, self.name, self.struct_name, self.name))
else:
result += '\n pb_membersize(%s, %s),' % (self.struct_name, self.name)
result += ' 0,'
if self.ltype == 'PB_LTYPE_SUBMESSAGE':
result += '\n &%s_fields}' % self.submsgname
elif self.default is None or self.htype == 'PB_HTYPE_CALLBACK':
result += ' 0}'
else:
result += '\n &%s_default}' % (self.struct_name + self.name)
result += '&%s_default)' % (self.struct_name + self.name)
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':
if self.pbtype == 'MESSAGE':
if self.rules == 'REPEATED' and self.allocation == 'STATIC':
return 'pb_membersize(%s, %s[0])' % (self.struct_name, self.name)
else:
return 'pb_membersize(%s, %s)' % (self.struct_name, self.name)
@@ -300,7 +283,14 @@ class Field:
class Message:
def __init__(self, names, desc, message_options):
self.name = names
self.fields = [Field(self.name, f, get_nanopb_suboptions(f, message_options)) for f in desc.field]
self.fields = []
for f in desc.field:
field_options = get_nanopb_suboptions(f, message_options)
if field_options.type != nanopb_pb2.FT_IGNORE:
self.fields.append(Field(self.name, f, field_options))
self.packed = message_options.packed_struct
self.ordered_fields = self.fields[:]
self.ordered_fields.sort()
@@ -311,7 +301,12 @@ class Message:
def __str__(self):
result = 'typedef struct _%s {\n' % self.name
result += '\n'.join([str(f) for f in self.ordered_fields])
result += '\n} %s;' % self.name
result += '\n}'
if self.packed:
result += ' pb_packed'
result += ' %s;' % self.name
return result
def types(self):
@@ -340,7 +335,7 @@ class Message:
prev = None
for field in self.ordered_fields:
result += field.pb_field_t(prev)
result += ',\n\n'
result += ',\n'
prev = field.name
result += ' PB_LAST_FIELD\n};'
@@ -389,7 +384,17 @@ def parse_file(fdesc, file_options):
message_options = get_nanopb_suboptions(message, file_options)
messages.append(Message(names, message, message_options))
for enum in message.enum_type:
enums.append(Enum(names, enum, message_options))
enum_options = get_nanopb_suboptions(enum, message_options)
enums.append(Enum(names, enum, enum_options))
# Fix field default values where enum short names are used.
for enum in enums:
if not enum.options.long_names:
for message in messages:
for field in message.fields:
if field.default in enum.value_longnames:
idx = enum.value_longnames.index(field.default)
field.default = enum.values[idx][0]
return enums, messages
@@ -424,6 +429,16 @@ def sort_dependencies(messages):
if msgname in message_by_name:
yield message_by_name[msgname]
def make_identifier(headername):
'''Make #ifndef identifier that contains uppercase A-Z and digits 0-9'''
result = ""
for c in headername.upper():
if c.isalnum():
result += c
else:
result += '_'
return result
def generate_header(dependencies, headername, enums, messages):
'''Generate content for a header file.
Generates strings, which should be concatenated and stored to file.
@@ -432,7 +447,7 @@ def generate_header(dependencies, headername, enums, messages):
yield '/* Automatically generated nanopb header */\n'
yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
symbol = headername.replace('.', '_').upper()
symbol = make_identifier(headername)
yield '#ifndef _PB_%s_\n' % symbol
yield '#define _PB_%s_\n' % symbol
yield '#include <pb.h>\n\n'
@@ -463,50 +478,6 @@ def generate_header(dependencies, headername, enums, messages):
for msg in messages:
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'
yield '\n#ifdef __cplusplus\n'
yield '} /* extern "C" */\n'
yield '#endif\n'
@@ -528,7 +499,72 @@ def generate_source(headername, enums, messages):
for msg in messages:
yield msg.fields_definition() + '\n\n'
if messages:
count_required_fields = lambda m: len([f for f in msg.fields if f.rules == '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'
# Add checks for numeric limits
worst = 0
worst_field = ''
checks = []
checks_msgnames = []
for msg in messages:
checks_msgnames.append(msg.name)
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)
msgs = '_'.join(str(n) for n in checks_msgnames)
yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
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)
msgs = '_'.join(str(n) for n in checks_msgnames)
yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
yield '#endif\n'
# Add check for sizeof(double)
has_double = False
for msg in messages:
for field in msg.fields:
if field.ctype == 'double':
has_double = True
if has_double:
yield '\n'
yield '/* On some platforms (such as AVR), double is really float.\n'
yield ' * These are not directly supported by nanopb, but see example_avr_double.\n'
yield ' * To get rid of this error, remove any double fields from your .proto.\n'
yield ' */\n'
yield 'STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n'
yield '\n'
# ---------------------------------------------------------------------------

View File

@@ -12,7 +12,7 @@ import google.protobuf.descriptor_pb2
DESCRIPTOR = descriptor.FileDescriptor(
name='nanopb.proto',
package='',
serialized_pb='\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"t\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true*;\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\r\n\tFT_STATIC\x10\x02:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions')
serialized_pb='\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\x92\x01\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse*J\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions')
_FIELDTYPE = descriptor.EnumDescriptor(
name='FieldType',
@@ -32,17 +32,22 @@ _FIELDTYPE = descriptor.EnumDescriptor(
name='FT_STATIC', index=2, number=2,
options=None,
type=None),
descriptor.EnumValueDescriptor(
name='FT_IGNORE', index=3, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=168,
serialized_end=227,
serialized_start=199,
serialized_end=273,
)
FT_DEFAULT = 0
FT_CALLBACK = 1
FT_STATIC = 2
FT_IGNORE = 3
NANOPB_FILEOPT_FIELD_NUMBER = 1010
nanopb_fileopt = descriptor.FieldDescriptor(
@@ -113,6 +118,13 @@ _NANOPBOPTIONS = descriptor.Descriptor(
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='packed_struct', full_name='NanoPBOptions.packed_struct', index=4,
number=5, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
@@ -122,8 +134,8 @@ _NANOPBOPTIONS = descriptor.Descriptor(
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=50,
serialized_end=166,
serialized_start=51,
serialized_end=197,
)
_NANOPBOPTIONS.fields_by_name['type'].enum_type = _FIELDTYPE

188
pb.h
View File

@@ -6,7 +6,7 @@
* see pb_encode.h or pb_decode.h
*/
#define NANOPB_VERSION nanopb-0.1.7
#define NANOPB_VERSION nanopb-0.2.0-dev
#include <stdint.h>
#include <stddef.h>
@@ -24,9 +24,13 @@
#define UNUSED(x) (void)(x)
#endif
/* Compile-time assertion, used for checking compatible compilation options. */
/* Compile-time assertion, used for checking compatible compilation options.
* If this fails on your compiler for some reason, use #define STATIC_ASSERT
* to disable it. */
#ifndef STATIC_ASSERT
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1];
#define STATIC_ASSERT(COND,MSG) typedef char STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
#define STATIC_ASSERT_MSG(MSG, LINE, COUNTER) STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
#define STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) static_assertion_##MSG##LINE##COUNTER
#endif
/* Number of required fields to keep track of
@@ -49,60 +53,55 @@
* SINT* is different, though, because it is zig-zag coded.
*/
typedef enum {
/************************
* Field contents types *
************************/
/* Numeric types */
PB_LTYPE_VARINT = 0x00, /* int32, uint32, int64, uint64, bool, enum */
PB_LTYPE_SVARINT = 0x01, /* sint32, sint64 */
PB_LTYPE_FIXED32 = 0x02, /* fixed32, sfixed32, float */
PB_LTYPE_FIXED64 = 0x03, /* fixed64, sfixed64, double */
/* Marker for last packable field type. */
PB_LTYPE_LAST_PACKABLE = 0x03,
/* Byte array with pre-allocated buffer.
* data_size is the length of the allocated PB_BYTES_ARRAY structure. */
PB_LTYPE_BYTES = 0x04,
/* String with pre-allocated buffer.
* data_size is the maximum length. */
PB_LTYPE_STRING = 0x05,
/* Submessage
* submsg_fields is pointer to field descriptions */
PB_LTYPE_SUBMESSAGE = 0x06,
/* Number of declared LTYPES */
PB_LTYPES_COUNT = 7,
PB_LTYPE_MASK = 0x0F,
/******************
* Modifier flags *
******************/
/* Just the basic, write data at data_offset */
PB_HTYPE_REQUIRED = 0x00,
/* Write true at size_offset */
PB_HTYPE_OPTIONAL = 0x10,
/* Read to pre-allocated array
* Maximum number of entries is array_size,
* actual number is stored at size_offset */
PB_HTYPE_ARRAY = 0x20,
/* Works for all required/optional/repeated fields.
* data_offset points to pb_callback_t structure.
* LTYPE should be 0 (it is ignored, but sometimes
* used to speculatively index an array). */
PB_HTYPE_CALLBACK = 0x30,
PB_HTYPE_MASK = 0xF0
} pb_packed pb_type_t;
typedef uint8_t pb_type_t;
/************************
* Field contents types *
************************/
/* Numeric types */
#define PB_LTYPE_VARINT 0x00 /* int32, uint32, int64, uint64, bool, enum */
#define PB_LTYPE_SVARINT 0x01 /* sint32, sint64 */
#define PB_LTYPE_FIXED32 0x02 /* fixed32, sfixed32, float */
#define PB_LTYPE_FIXED64 0x03 /* fixed64, sfixed64, double */
/* Marker for last packable field type. */
#define PB_LTYPE_LAST_PACKABLE 0x03
/* Byte array with pre-allocated buffer.
* data_size is the length of the allocated PB_BYTES_ARRAY structure. */
#define PB_LTYPE_BYTES 0x04
/* String with pre-allocated buffer.
* data_size is the maximum length. */
#define PB_LTYPE_STRING 0x05
/* Submessage
* submsg_fields is pointer to field descriptions */
#define PB_LTYPE_SUBMESSAGE 0x06
/* Number of declared LTYPES */
#define PB_LTYPES_COUNT 7
#define PB_LTYPE_MASK 0x0F
/**************************
* Field repetition rules *
**************************/
#define PB_HTYPE_REQUIRED 0x00
#define PB_HTYPE_OPTIONAL 0x10
#define PB_HTYPE_REPEATED 0x20
#define PB_HTYPE_MASK 0x30
/********************
* Allocation types *
********************/
#define PB_ATYPE_STATIC 0x00
#define PB_ATYPE_CALLBACK 0x40
#define PB_ATYPE_MASK 0xC0
#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
@@ -200,9 +199,84 @@ typedef enum {
#define pb_membersize(st, m) (sizeof ((st*)0)->m)
#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_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) \
? offsetof(st, m1) \
: offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2))
#define PB_LAST_FIELD {0,(pb_type_t) 0,0,0,0,0,0}
/* Required fields are the simplest. They just have delta (padding) from
* previous field end, and the size of the field. Pointer is used for
* submessages and default values.
*/
#define PB_REQUIRED_STATIC(tag, st, m, pm, ltype, ptr) \
{tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
/* Optional fields add the delta to the has_ variable. */
#define PB_OPTIONAL_STATIC(tag, st, m, pm, ltype, ptr) \
{tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
pb_delta_end(st, m, pm), \
pb_delta(st, has_ ## m, m), \
pb_membersize(st, m), 0, ptr}
/* Repeated fields have a _count field and also the maximum number of entries. */
#define PB_REPEATED_STATIC(tag, st, m, pm, ltype, ptr) \
{tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, \
pb_delta_end(st, m, pm), \
pb_delta(st, m ## _count, m), \
pb_membersize(st, m[0]), \
pb_arraysize(st, m), ptr}
/* Callbacks are much like required fields except with special datatype. */
#define PB_REQUIRED_CALLBACK(tag, st, m, pm, ltype, ptr) \
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
#define PB_OPTIONAL_CALLBACK(tag, st, m, pm, ltype, ptr) \
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
#define PB_REPEATED_CALLBACK(tag, st, m, pm, ltype, ptr) \
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
/* The mapping from protobuf types to LTYPEs is done using these macros. */
#define PB_LTYPE_MAP_BOOL PB_LTYPE_VARINT
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT
#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT
#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE
#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT
#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT
#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING
#define PB_LTYPE_MAP_UINT32 PB_LTYPE_VARINT
#define PB_LTYPE_MAP_UINT64 PB_LTYPE_VARINT
/* This is the actual macro used in field descriptions.
* It takes these arguments:
* - Field tag number
* - Field type: BOOL, BYTES, DOUBLE, ENUM, FIXED32, FIXED64,
* FLOAT, INT32, INT64, MESSAGE, SFIXED32, SFIXED64
* SINT32, SINT64, STRING, UINT32 or UINT64
* - Field rules: REQUIRED, OPTIONAL or REPEATED
* - Allocation: STATIC or CALLBACK
* - Message name
* - Field name
* - Previous field name (or field name again for first field)
* - Pointer to default value or submsg fields.
*/
#define PB_FIELD(tag, type, rules, allocation, message, field, prevfield, ptr) \
PB_ ## rules ## _ ## allocation(tag, message, field, prevfield, \
PB_LTYPE_MAP_ ## type, ptr)
/* These macros are used for giving out error messages.
* They are mostly a debugging aid; the main error information
* is the true/false return value from functions.

View File

@@ -39,16 +39,20 @@ static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count)
{
uint8_t *source = (uint8_t*)stream->state;
stream->state = source + count;
if (buf != NULL)
memcpy(buf, source, count);
{
while (count--)
*buf++ = *source++;
}
stream->state = source + count;
return true;
}
bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
{
#ifndef PB_BUFFER_ONLY
if (buf == NULL && stream->callback != buf_read)
{
/* Skip input bytes */
@@ -63,12 +67,18 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
return pb_read(stream, tmp, count);
}
#endif
if (stream->bytes_left < count)
PB_RETURN_ERROR(stream, "end-of-stream");
#ifndef PB_BUFFER_ONLY
if (!stream->callback(stream, buf, count))
PB_RETURN_ERROR(stream, "io error");
#else
if (!buf_read(stream, buf, count))
return false;
#endif
stream->bytes_left -= count;
return true;
@@ -77,9 +87,16 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize)
{
pb_istream_t stream;
#ifdef PB_BUFFER_ONLY
stream.callback = NULL;
#else
stream.callback = &buf_read;
#endif
stream.state = buf;
stream.bytes_left = bufsize;
#ifndef PB_NO_ERRMSG
stream.errmsg = NULL;
#endif
return stream;
}
@@ -89,28 +106,60 @@ pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize)
static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
{
uint64_t temp;
bool status = pb_decode_varint(stream, &temp);
*dest = (uint32_t)temp;
return status;
uint8_t byte;
uint32_t result;
if (!pb_read(stream, &byte, 1))
return false;
if (!(byte & 0x80))
{
/* Quick case, 1 byte value */
result = byte;
}
else
{
/* Multibyte case */
uint8_t bitpos = 7;
result = byte & 0x7F;
do
{
if (bitpos >= 32)
PB_RETURN_ERROR(stream, "varint overflow");
if (!pb_read(stream, &byte, 1))
return false;
result |= (uint32_t)(byte & 0x7F) << bitpos;
bitpos = (uint8_t)(bitpos + 7);
} while (byte & 0x80);
}
*dest = result;
return true;
}
bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest)
{
uint8_t byte;
int bitpos = 0;
*dest = 0;
uint8_t bitpos = 0;
uint64_t result = 0;
while (bitpos < 64 && pb_read(stream, &byte, 1))
do
{
*dest |= (uint64_t)(byte & 0x7F) << bitpos;
bitpos += 7;
if (bitpos >= 64)
PB_RETURN_ERROR(stream, "varint overflow");
if (!(byte & 0x80))
return true;
}
if (!pb_read(stream, &byte, 1))
return false;
result |= (uint64_t)(byte & 0x7F) << bitpos;
bitpos = (uint8_t)(bitpos + 7);
} while (byte & 0x80);
PB_RETURN_ERROR(stream, "varint overflow");
*dest = result;
return true;
}
bool checkreturn pb_skip_varint(pb_istream_t *stream)
@@ -222,14 +271,18 @@ bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *su
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream)
{
stream->state = substream->state;
#ifndef PB_NO_ERRMSG
stream->errmsg = substream->errmsg;
#endif
}
/* Iterator for pb_field_t list */
typedef struct {
const pb_field_t *start; /* Start of the pb_field_t array */
const pb_field_t *current; /* Current position of the iterator */
int field_index; /* Zero-based index of the field. */
int required_field_index; /* Zero-based index that counts only the required fields */
unsigned field_index; /* Zero-based index of the field. */
unsigned required_field_index; /* Zero-based index that counts only the required fields */
void *dest_struct; /* Pointer to the destination structure to decode to */
void *pData; /* Pointer where to store current field value */
void *pSize; /* Pointer where to store the size of current array field */
@@ -250,8 +303,11 @@ static bool pb_field_next(pb_field_iterator_t *iter)
bool notwrapped = true;
size_t prev_size = iter->current->data_size;
if (PB_HTYPE(iter->current->type) == PB_HTYPE_ARRAY)
if (PB_ATYPE(iter->current->type) == PB_ATYPE_STATIC &&
PB_HTYPE(iter->current->type) == PB_HTYPE_REPEATED)
{
prev_size *= iter->current->array_size;
}
if (PB_HTYPE(iter->current->type) == PB_HTYPE_REQUIRED)
iter->required_field_index++;
@@ -275,7 +331,7 @@ static bool pb_field_next(pb_field_iterator_t *iter)
static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag)
{
int start = iter->field_index;
unsigned start = iter->field_index;
do {
if (iter->current->tag == tag)
@@ -290,11 +346,15 @@ static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag)
* Decode a single field *
*************************/
static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
{
pb_decoder_t func = PB_DECODERS[PB_LTYPE(iter->current->type)];
pb_type_t type;
pb_decoder_t func;
switch (PB_HTYPE(iter->current->type))
type = iter->current->type;
func = PB_DECODERS[PB_LTYPE(type)];
switch (PB_HTYPE(type))
{
case PB_HTYPE_REQUIRED:
return func(stream, iter->current, iter->pData);
@@ -303,12 +363,12 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
*(bool*)iter->pSize = true;
return func(stream, iter->current, iter->pData);
case PB_HTYPE_ARRAY:
case PB_HTYPE_REPEATED:
if (wire_type == PB_WT_STRING
&& PB_LTYPE(iter->current->type) <= PB_LTYPE_LAST_PACKABLE)
&& PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
{
/* Packed array */
bool status;
bool status = true;
size_t *size = (size_t*)iter->pSize;
pb_istream_t substream;
if (!pb_make_string_substream(stream, &substream))
@@ -318,11 +378,17 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
{
void *pItem = (uint8_t*)iter->pData + iter->current->data_size * (*size);
if (!func(&substream, iter->current, pItem))
return false;
{
status = false;
break;
}
(*size)++;
}
status = (substream.bytes_left == 0);
pb_close_string_substream(stream, &substream);
if (substream.bytes_left != 0)
PB_RETURN_ERROR(stream, "array overflow");
return status;
}
else
@@ -336,48 +402,63 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
(*size)++;
return func(stream, iter->current, pItem);
}
default:
PB_RETURN_ERROR(stream, "invalid field type");
}
}
static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
{
pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
if (pCallback->funcs.decode == NULL)
return pb_skip_field(stream, wire_type);
if (wire_type == PB_WT_STRING)
{
pb_istream_t substream;
case PB_HTYPE_CALLBACK:
if (!pb_make_string_substream(stream, &substream))
return false;
while (substream.bytes_left)
{
pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
if (pCallback->funcs.decode == NULL)
return pb_skip_field(stream, wire_type);
if (wire_type == PB_WT_STRING)
{
pb_istream_t substream;
if (!pb_make_string_substream(stream, &substream))
return false;
while (substream.bytes_left)
{
if (!pCallback->funcs.decode(&substream, iter->current, pCallback->arg))
PB_RETURN_ERROR(stream, "callback failed");
}
pb_close_string_substream(stream, &substream);
return true;
}
else
{
/* Copy the single scalar value to stack.
* This is required so that we can limit the stream length,
* which in turn allows to use same callback for packed and
* not-packed fields. */
pb_istream_t substream;
uint8_t buffer[10];
size_t size = sizeof(buffer);
if (!read_raw_value(stream, wire_type, buffer, &size))
return false;
substream = pb_istream_from_buffer(buffer, size);
return pCallback->funcs.decode(&substream, iter->current, pCallback->arg);
}
if (!pCallback->funcs.decode(&substream, iter->current, pCallback->arg))
PB_RETURN_ERROR(stream, "callback failed");
}
pb_close_string_substream(stream, &substream);
return true;
}
else
{
/* Copy the single scalar value to stack.
* This is required so that we can limit the stream length,
* which in turn allows to use same callback for packed and
* not-packed fields. */
pb_istream_t substream;
uint8_t buffer[10];
size_t size = sizeof(buffer);
if (!read_raw_value(stream, wire_type, buffer, &size))
return false;
substream = pb_istream_from_buffer(buffer, size);
return pCallback->funcs.decode(&substream, iter->current, pCallback->arg);
}
}
static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
{
switch (PB_ATYPE(iter->current->type))
{
case PB_ATYPE_STATIC:
return decode_static_field(stream, wire_type, iter);
case PB_ATYPE_CALLBACK:
return decode_callback_field(stream, wire_type, iter);
default:
PB_RETURN_ERROR(stream, "invalid field type");
}
@@ -392,37 +473,43 @@ static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_str
/* Initialize size/has fields and apply default values */
do
{
pb_type_t type;
type = iter.current->type;
if (iter.current->tag == 0)
continue;
/* Initialize the size field for optional/repeated fields to 0. */
if (PB_HTYPE(iter.current->type) == PB_HTYPE_OPTIONAL)
if (PB_ATYPE(type) == PB_ATYPE_STATIC)
{
*(bool*)iter.pSize = false;
/* Initialize the size field for optional/repeated fields to 0. */
if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL)
{
*(bool*)iter.pSize = false;
}
else if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
{
*(size_t*)iter.pSize = 0;
continue; /* Array is empty, no need to initialize contents */
}
/* Initialize field contents to default value */
if (PB_LTYPE(iter.current->type) == PB_LTYPE_SUBMESSAGE)
{
pb_message_set_to_defaults((const pb_field_t *) iter.current->ptr, iter.pData);
}
else if (iter.current->ptr != NULL)
{
memcpy(iter.pData, iter.current->ptr, iter.current->data_size);
}
else
{
memset(iter.pData, 0, iter.current->data_size);
}
}
else if (PB_HTYPE(iter.current->type) == PB_HTYPE_ARRAY)
{
*(size_t*)iter.pSize = 0;
continue; /* Array is empty, no need to initialize contents */
}
/* Initialize field contents to default value */
if (PB_HTYPE(iter.current->type) == PB_HTYPE_CALLBACK)
else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
{
continue; /* Don't overwrite callback */
}
else if (PB_LTYPE(iter.current->type) == PB_LTYPE_SUBMESSAGE)
{
pb_message_set_to_defaults((const pb_field_t *) iter.current->ptr, iter.pData);
}
else if (iter.current->ptr != NULL)
{
memcpy(iter.pData, iter.current->ptr, iter.current->data_size);
}
else
{
memset(iter.pData, 0, iter.current->data_size);
}
} while (pb_field_next(&iter));
}
@@ -470,15 +557,34 @@ bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[
}
/* Check that all required fields were present. */
pb_field_init(&iter, fields, dest_struct);
do {
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))))
{
/* First figure out the number of required fields by
* seeking to the end of the field array. Usually we
* are already close to end after decoding.
*/
unsigned req_field_count;
pb_type_t last_type;
unsigned i;
do {
req_field_count = iter.required_field_index;
last_type = iter.current->type;
} while (pb_field_next(&iter));
/* Fixup if last field was also required. */
if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED)
req_field_count++;
/* Check the whole bytes */
for (i = 0; i < (req_field_count >> 3); i++)
{
PB_RETURN_ERROR(stream, "missing required field");
if (fields_seen[i] != 0xFF)
PB_RETURN_ERROR(stream, "missing required field");
}
} while (pb_field_next(&iter));
/* Check the remaining bits */
if (fields_seen[req_field_count >> 3] != (0xFF >> (8 - (req_field_count & 7))))
PB_RETURN_ERROR(stream, "missing required field");
}
return true;
}
@@ -637,7 +743,7 @@ bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field
/* New array entries need to be initialized, while required and optional
* submessages have already been initialized in the top-level pb_decode. */
if (PB_HTYPE(field->type) == PB_HTYPE_ARRAY)
if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
status = pb_decode(&substream, submsg_fields, dest);
else
status = pb_decode_noinit(&substream, submsg_fields, dest);

View File

@@ -32,7 +32,16 @@ extern "C" {
*/
struct _pb_istream_t
{
#ifdef PB_BUFFER_ONLY
/* Callback pointer is not used in buffer-only configuration.
* Having an int pointer here allows binary compatibility but
* gives an error if someone tries to assign callback function.
*/
int *callback;
#else
bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count);
#endif
void *state; /* Free field for use by callback implementation */
size_t bytes_left;

View File

@@ -37,15 +37,22 @@ static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{
uint8_t *dest = (uint8_t*)stream->state;
memcpy(dest, buf, count);
stream->state = dest + count;
while (count--)
*dest++ = *buf++;
return true;
}
pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize)
{
pb_ostream_t stream;
#ifdef PB_BUFFER_ONLY
stream.callback = (void*)1; /* Just some marker value */
#else
stream.callback = &buf_write;
#endif
stream.state = buf;
stream.max_size = bufsize;
stream.bytes_written = 0;
@@ -58,9 +65,14 @@ bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count
{
if (stream->bytes_written + count > stream->max_size)
return false;
#ifdef PB_BUFFER_ONLY
if (!buf_write(stream, buf, count))
return false;
#else
if (!stream->callback(stream, buf, count))
return false;
#endif
}
stream->bytes_written += count;
@@ -141,58 +153,89 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
return true;
}
bool checkreturn encode_static_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData)
{
pb_encoder_t func;
const void *pSize;
func = PB_ENCODERS[PB_LTYPE(field->type)];
pSize = (const char*)pData + field->size_offset;
switch (PB_HTYPE(field->type))
{
case PB_HTYPE_REQUIRED:
if (!pb_encode_tag_for_field(stream, field))
return false;
if (!func(stream, field, pData))
return false;
break;
case PB_HTYPE_OPTIONAL:
if (*(const bool*)pSize)
{
if (!pb_encode_tag_for_field(stream, field))
return false;
if (!func(stream, field, pData))
return false;
}
break;
case PB_HTYPE_REPEATED:
if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
return false;
break;
default:
return false;
}
return true;
}
bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData)
{
const pb_callback_t *callback = (const pb_callback_t*)pData;
if (callback->funcs.encode != NULL)
{
if (!callback->funcs.encode(stream, field, callback->arg))
return false;
}
return true;
}
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
{
const pb_field_t *field = fields;
const void *pData = src_struct;
const void *pSize;
size_t prev_size = 0;
while (field->tag != 0)
{
pb_encoder_t func = PB_ENCODERS[PB_LTYPE(field->type)];
pData = (const char*)pData + prev_size + field->data_offset;
pSize = (const char*)pData + field->size_offset;
prev_size = field->data_size;
if (PB_HTYPE(field->type) == PB_HTYPE_ARRAY)
prev_size *= field->array_size;
switch (PB_HTYPE(field->type))
/* Special case for static arrays */
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
{
case PB_HTYPE_REQUIRED:
if (!pb_encode_tag_for_field(stream, field))
return false;
if (!func(stream, field, pData))
return false;
break;
case PB_HTYPE_OPTIONAL:
if (*(const bool*)pSize)
{
if (!pb_encode_tag_for_field(stream, field))
return false;
prev_size *= field->array_size;
}
if (!func(stream, field, pData))
return false;
}
break;
case PB_HTYPE_ARRAY:
if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
switch (PB_ATYPE(field->type))
{
case PB_ATYPE_STATIC:
if (!encode_static_field(stream, field, pData))
return false;
break;
case PB_HTYPE_CALLBACK:
{
const pb_callback_t *callback = (const pb_callback_t*)pData;
if (callback->funcs.encode != NULL)
{
if (!callback->funcs.encode(stream, field, callback->arg))
return false;
}
case PB_ATYPE_CALLBACK:
if (!encode_callback_field(stream, field, pData))
return false;
break;
}
default:
return false;
}
field++;

View File

@@ -32,7 +32,17 @@ extern "C" {
*/
struct _pb_ostream_t
{
#ifdef PB_BUFFER_ONLY
/* Callback pointer is not used in buffer-only configuration.
* Having an int pointer here allows binary compatibility but
* gives an error if someone tries to assign callback function.
* Also, NULL pointer marks a 'sizing stream' that does not
* write anything.
*/
int *callback;
#else
bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count);
#endif
void *state; /* Free field for use by callback implementation */
size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */
size_t bytes_written;

View File

@@ -1,15 +1,31 @@
CFLAGS=-ansi -Wall -Werror -I .. -g -O0 --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 missing_fields.pb.h
TESTS=test_decode1 test_encode1 decode_unittests encode_unittests test_no_messages
CFLAGS=-ansi -Wall -Werror -I .. -g -O0
DEPS=../pb_decode.h ../pb_encode.h ../pb.h person.pb.h \
callbacks2.pb.h callbacks.pb.h unittests.h unittestproto.pb.h \
alltypes.pb.h missing_fields.pb.h
TESTS= decode_unittests encode_unittests \
test_decode1 test_decode2 test_decode3 test_decode3_buf \
test_encode1 test_encode2 test_encode3 test_encode3_buf \
test_decode_callbacks test_encode_callbacks \
test_missing_fields test_no_messages test_funny_name \
test_multiple_files test_cxxcompile test_options \
bc_encode bc_decode
# More strict checks for the core part of nanopb
CFLAGS_CORE=-pedantic -Wextra -Wcast-qual -Wlogical-op -Wconversion
CC_VERSION=$(shell $(CC) -v 2>&1)
CFLAGS_CORE=
ifneq (,$(findstring gcc,$(CC_VERSION)))
CFLAGS_CORE=-pedantic -Wextra -Wcast-qual -Wlogical-op -Wconversion
CFLAGS+=--coverage
LDFLAGS+=--coverage
endif
ifneq (,$(findstring clang,$(CC_VERSION)))
CFLAGS_CORE=-pedantic -Wextra -Wcast-qual -Wconversion
endif
all: breakpoints $(TESTS) run_unittests
clean:
rm -f $(TESTS) person.pb* alltypes.pb* *.o *.gcda *.gcno
rm -f $(TESTS) person.pb* alltypes.pb* *.o *.gcda *.gcno *.pb.h *.pb.c
%.pb.o: %.pb.c %.pb.h
$(CC) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
@@ -23,11 +39,26 @@ pb_encode.o: ../pb_encode.c $(DEPS)
pb_decode.o: ../pb_decode.c $(DEPS)
$(CC) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
# Test for compilability with c++ compiler
pb_encode.cxx.o: ../pb_encode.c $(DEPS)
$(CXX) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
pb_decode.cxx.o: ../pb_decode.c $(DEPS)
$(CXX) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
# Test for PB_BUF_ONLY compilation option
pb_encode.buf.o: ../pb_encode.c $(DEPS)
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
pb_decode.buf.o: ../pb_decode.c $(DEPS)
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
%.buf.o: %.c $(DEPS)
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) -c -o $@ $<
test_encode3_buf: test_encode3.buf.o pb_encode.buf.o alltypes.pb.o
$(CC) $(LDFLAGS) $^ -o $@
test_decode3_buf: test_decode3.buf.o pb_decode.buf.o alltypes.pb.o
$(CC) $(LDFLAGS) $^ -o $@
test_cxxcompile: pb_encode.cxx.o pb_decode.cxx.o
test_decode1: test_decode1.o pb_decode.o person.pb.o
test_decode2: test_decode2.o pb_decode.o person.pb.o
@@ -35,12 +66,16 @@ test_decode3: test_decode3.o pb_decode.o alltypes.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_encode3: test_encode3.o pb_encode.o alltypes.pb.o
test_multiple_files: test_multiple_files.o pb_encode.o callbacks2.pb.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_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
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
test_funny_name: funny-proto+name.pb.h funny-proto+name.pb.o
bc_encode: bc_alltypes.pb.o pb_encode.o bc_encode.o
bc_decode: bc_alltypes.pb.o pb_decode.o bc_decode.o
%.pb: %.proto
protoc -I. -I../generator -I/usr/include -o$@ $<
@@ -55,7 +90,7 @@ coverage: run_unittests
gcov pb_encode.gcda
gcov pb_decode.gcda
run_unittests: decode_unittests encode_unittests test_cxxcompile test_encode1 test_encode2 test_encode3 test_decode1 test_decode2 test_decode3 test_encode_callbacks test_decode_callbacks test_missing_fields test_options
run_unittests: $(TESTS)
rm -f *.gcda
./decode_unittests > /dev/null
@@ -79,10 +114,12 @@ run_unittests: decode_unittests encode_unittests test_cxxcompile test_encode1 te
./test_encode3 | ./test_decode3
./test_encode3 1 | ./test_decode3 1
./test_encode3 1 | protoc --decode=AllTypes -I. -I../generator -I/usr/include alltypes.proto >/dev/null
./test_encode3_buf 1 | ./test_decode3_buf 1
./bc_encode | ./bc_decode
./test_missing_fields
test_options: options.pb.h options.expected
test_options: options.pb.h options.expected options.pb.o
cat options.expected | while read -r p; do \
if ! grep -q "$$p" $<; then \
echo Expected: "$$p"; \

93
tests/bc_alltypes.pb.c Normal file
View File

@@ -0,0 +1,93 @@
/* Automatically generated nanopb constant definitions */
/* Generated by 0.2.0-dev at Sun Feb 17 00:09:53 2013. */
/* This is a file generated using nanopb-0.2.0-dev.
* It is used as a part of test suite in order to detect any
* incompatible changes made to the generator in future versions.
*/
#include "alltypes.pb.h"
const char SubMessage_substuff1_default[16] = "1";
const int32_t SubMessage_substuff2_default = 2;
const uint32_t SubMessage_substuff3_default = 3;
const int32_t AllTypes_opt_int32_default = 4041;
const int64_t AllTypes_opt_int64_default = 4042;
const uint32_t AllTypes_opt_uint32_default = 4043;
const uint64_t AllTypes_opt_uint64_default = 4044;
const int32_t AllTypes_opt_sint32_default = 4045;
const int64_t AllTypes_opt_sint64_default = 4046;
const bool AllTypes_opt_bool_default = false;
const uint32_t AllTypes_opt_fixed32_default = 4048;
const int32_t AllTypes_opt_sfixed32_default = 4049;
const float AllTypes_opt_float_default = 4050;
const uint64_t AllTypes_opt_fixed64_default = 4051;
const int64_t AllTypes_opt_sfixed64_default = 4052;
const double AllTypes_opt_double_default = 4053;
const char AllTypes_opt_string_default[16] = "4054";
const AllTypes_opt_bytes_t AllTypes_opt_bytes_default = {4, {0x34,0x30,0x35,0x35}};
const MyEnum AllTypes_opt_enum_default = MyEnum_Second;
const pb_field_t SubMessage_fields[4] = {
PB_FIELD( 1, STRING , REQUIRED, STATIC, SubMessage, substuff1, substuff1, &SubMessage_substuff1_default),
PB_FIELD( 2, INT32 , REQUIRED, STATIC, SubMessage, substuff2, substuff1, &SubMessage_substuff2_default),
PB_FIELD( 3, FIXED32 , OPTIONAL, STATIC, SubMessage, substuff3, substuff2, &SubMessage_substuff3_default),
PB_LAST_FIELD
};
const pb_field_t AllTypes_fields[53] = {
PB_FIELD( 1, INT32 , REQUIRED, STATIC, AllTypes, req_int32, req_int32, 0),
PB_FIELD( 2, INT64 , REQUIRED, STATIC, AllTypes, req_int64, req_int32, 0),
PB_FIELD( 3, UINT32 , REQUIRED, STATIC, AllTypes, req_uint32, req_int64, 0),
PB_FIELD( 4, UINT64 , REQUIRED, STATIC, AllTypes, req_uint64, req_uint32, 0),
PB_FIELD( 5, SINT32 , REQUIRED, STATIC, AllTypes, req_sint32, req_uint64, 0),
PB_FIELD( 6, SINT64 , REQUIRED, STATIC, AllTypes, req_sint64, req_sint32, 0),
PB_FIELD( 7, BOOL , REQUIRED, STATIC, AllTypes, req_bool, req_sint64, 0),
PB_FIELD( 8, FIXED32 , REQUIRED, STATIC, AllTypes, req_fixed32, req_bool, 0),
PB_FIELD( 9, SFIXED32, REQUIRED, STATIC, AllTypes, req_sfixed32, req_fixed32, 0),
PB_FIELD( 10, FLOAT , REQUIRED, STATIC, AllTypes, req_float, req_sfixed32, 0),
PB_FIELD( 11, FIXED64 , REQUIRED, STATIC, AllTypes, req_fixed64, req_float, 0),
PB_FIELD( 12, SFIXED64, REQUIRED, STATIC, AllTypes, req_sfixed64, req_fixed64, 0),
PB_FIELD( 13, DOUBLE , REQUIRED, STATIC, AllTypes, req_double, req_sfixed64, 0),
PB_FIELD( 14, STRING , REQUIRED, STATIC, AllTypes, req_string, req_double, 0),
PB_FIELD( 15, BYTES , REQUIRED, STATIC, AllTypes, req_bytes, req_string, 0),
PB_FIELD( 16, MESSAGE , REQUIRED, STATIC, AllTypes, req_submsg, req_bytes, &SubMessage_fields),
PB_FIELD( 17, ENUM , REQUIRED, STATIC, AllTypes, req_enum, req_submsg, 0),
PB_FIELD( 21, INT32 , REPEATED, STATIC, AllTypes, rep_int32, req_enum, 0),
PB_FIELD( 22, INT64 , REPEATED, STATIC, AllTypes, rep_int64, rep_int32, 0),
PB_FIELD( 23, UINT32 , REPEATED, STATIC, AllTypes, rep_uint32, rep_int64, 0),
PB_FIELD( 24, UINT64 , REPEATED, STATIC, AllTypes, rep_uint64, rep_uint32, 0),
PB_FIELD( 25, SINT32 , REPEATED, STATIC, AllTypes, rep_sint32, rep_uint64, 0),
PB_FIELD( 26, SINT64 , REPEATED, STATIC, AllTypes, rep_sint64, rep_sint32, 0),
PB_FIELD( 27, BOOL , REPEATED, STATIC, AllTypes, rep_bool, rep_sint64, 0),
PB_FIELD( 28, FIXED32 , REPEATED, STATIC, AllTypes, rep_fixed32, rep_bool, 0),
PB_FIELD( 29, SFIXED32, REPEATED, STATIC, AllTypes, rep_sfixed32, rep_fixed32, 0),
PB_FIELD( 30, FLOAT , REPEATED, STATIC, AllTypes, rep_float, rep_sfixed32, 0),
PB_FIELD( 31, FIXED64 , REPEATED, STATIC, AllTypes, rep_fixed64, rep_float, 0),
PB_FIELD( 32, SFIXED64, REPEATED, STATIC, AllTypes, rep_sfixed64, rep_fixed64, 0),
PB_FIELD( 33, DOUBLE , REPEATED, STATIC, AllTypes, rep_double, rep_sfixed64, 0),
PB_FIELD( 34, STRING , REPEATED, STATIC, AllTypes, rep_string, rep_double, 0),
PB_FIELD( 35, BYTES , REPEATED, STATIC, AllTypes, rep_bytes, rep_string, 0),
PB_FIELD( 36, MESSAGE , REPEATED, STATIC, AllTypes, rep_submsg, rep_bytes, &SubMessage_fields),
PB_FIELD( 37, ENUM , REPEATED, STATIC, AllTypes, rep_enum, rep_submsg, 0),
PB_FIELD( 41, INT32 , OPTIONAL, STATIC, AllTypes, opt_int32, rep_enum, &AllTypes_opt_int32_default),
PB_FIELD( 42, INT64 , OPTIONAL, STATIC, AllTypes, opt_int64, opt_int32, &AllTypes_opt_int64_default),
PB_FIELD( 43, UINT32 , OPTIONAL, STATIC, AllTypes, opt_uint32, opt_int64, &AllTypes_opt_uint32_default),
PB_FIELD( 44, UINT64 , OPTIONAL, STATIC, AllTypes, opt_uint64, opt_uint32, &AllTypes_opt_uint64_default),
PB_FIELD( 45, SINT32 , OPTIONAL, STATIC, AllTypes, opt_sint32, opt_uint64, &AllTypes_opt_sint32_default),
PB_FIELD( 46, SINT64 , OPTIONAL, STATIC, AllTypes, opt_sint64, opt_sint32, &AllTypes_opt_sint64_default),
PB_FIELD( 47, BOOL , OPTIONAL, STATIC, AllTypes, opt_bool, opt_sint64, &AllTypes_opt_bool_default),
PB_FIELD( 48, FIXED32 , OPTIONAL, STATIC, AllTypes, opt_fixed32, opt_bool, &AllTypes_opt_fixed32_default),
PB_FIELD( 49, SFIXED32, OPTIONAL, STATIC, AllTypes, opt_sfixed32, opt_fixed32, &AllTypes_opt_sfixed32_default),
PB_FIELD( 50, FLOAT , OPTIONAL, STATIC, AllTypes, opt_float, opt_sfixed32, &AllTypes_opt_float_default),
PB_FIELD( 51, FIXED64 , OPTIONAL, STATIC, AllTypes, opt_fixed64, opt_float, &AllTypes_opt_fixed64_default),
PB_FIELD( 52, SFIXED64, OPTIONAL, STATIC, AllTypes, opt_sfixed64, opt_fixed64, &AllTypes_opt_sfixed64_default),
PB_FIELD( 53, DOUBLE , OPTIONAL, STATIC, AllTypes, opt_double, opt_sfixed64, &AllTypes_opt_double_default),
PB_FIELD( 54, STRING , OPTIONAL, STATIC, AllTypes, opt_string, opt_double, &AllTypes_opt_string_default),
PB_FIELD( 55, BYTES , OPTIONAL, STATIC, AllTypes, opt_bytes, opt_string, &AllTypes_opt_bytes_default),
PB_FIELD( 56, MESSAGE , OPTIONAL, STATIC, AllTypes, opt_submsg, opt_bytes, &SubMessage_fields),
PB_FIELD( 57, ENUM , OPTIONAL, STATIC, AllTypes, opt_enum, opt_submsg, &AllTypes_opt_enum_default),
PB_FIELD( 99, INT32 , REQUIRED, STATIC, AllTypes, end, opt_enum, 0),
PB_LAST_FIELD
};

178
tests/bc_alltypes.pb.h Normal file
View File

@@ -0,0 +1,178 @@
/* Automatically generated nanopb header */
/* This is a file generated using nanopb-0.2.0-dev.
* It is used as a part of test suite in order to detect any
* incompatible changes made to the generator in future versions.
*/
#ifndef _PB_ALLTYPES_PB_H_
#define _PB_ALLTYPES_PB_H_
#include <pb.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _MyEnum {
MyEnum_Zero = 0,
MyEnum_First = 1,
MyEnum_Second = 2,
MyEnum_Truth = 42
} MyEnum;
/* Struct definitions */
typedef struct _SubMessage {
char substuff1[16];
int32_t substuff2;
bool has_substuff3;
uint32_t substuff3;
} SubMessage;
typedef struct {
size_t size;
uint8_t bytes[16];
} AllTypes_req_bytes_t;
typedef struct {
size_t size;
uint8_t bytes[16];
} AllTypes_rep_bytes_t;
typedef struct {
size_t size;
uint8_t bytes[16];
} AllTypes_opt_bytes_t;
typedef struct _AllTypes {
int32_t req_int32;
int64_t req_int64;
uint32_t req_uint32;
uint64_t req_uint64;
int32_t req_sint32;
int64_t req_sint64;
bool req_bool;
uint32_t req_fixed32;
int32_t req_sfixed32;
float req_float;
uint64_t req_fixed64;
int64_t req_sfixed64;
double req_double;
char req_string[16];
AllTypes_req_bytes_t req_bytes;
SubMessage req_submsg;
MyEnum req_enum;
size_t rep_int32_count;
int32_t rep_int32[5];
size_t rep_int64_count;
int64_t rep_int64[5];
size_t rep_uint32_count;
uint32_t rep_uint32[5];
size_t rep_uint64_count;
uint64_t rep_uint64[5];
size_t rep_sint32_count;
int32_t rep_sint32[5];
size_t rep_sint64_count;
int64_t rep_sint64[5];
size_t rep_bool_count;
bool rep_bool[5];
size_t rep_fixed32_count;
uint32_t rep_fixed32[5];
size_t rep_sfixed32_count;
int32_t rep_sfixed32[5];
size_t rep_float_count;
float rep_float[5];
size_t rep_fixed64_count;
uint64_t rep_fixed64[5];
size_t rep_sfixed64_count;
int64_t rep_sfixed64[5];
size_t rep_double_count;
double rep_double[5];
size_t rep_string_count;
char rep_string[5][16];
size_t rep_bytes_count;
AllTypes_rep_bytes_t rep_bytes[5];
size_t rep_submsg_count;
SubMessage rep_submsg[5];
size_t rep_enum_count;
MyEnum rep_enum[5];
bool has_opt_int32;
int32_t opt_int32;
bool has_opt_int64;
int64_t opt_int64;
bool has_opt_uint32;
uint32_t opt_uint32;
bool has_opt_uint64;
uint64_t opt_uint64;
bool has_opt_sint32;
int32_t opt_sint32;
bool has_opt_sint64;
int64_t opt_sint64;
bool has_opt_bool;
bool opt_bool;
bool has_opt_fixed32;
uint32_t opt_fixed32;
bool has_opt_sfixed32;
int32_t opt_sfixed32;
bool has_opt_float;
float opt_float;
bool has_opt_fixed64;
uint64_t opt_fixed64;
bool has_opt_sfixed64;
int64_t opt_sfixed64;
bool has_opt_double;
double opt_double;
bool has_opt_string;
char opt_string[16];
bool has_opt_bytes;
AllTypes_opt_bytes_t opt_bytes;
bool has_opt_submsg;
SubMessage opt_submsg;
bool has_opt_enum;
MyEnum opt_enum;
int32_t end;
} AllTypes;
/* Default values for struct fields */
extern const char SubMessage_substuff1_default[16];
extern const int32_t SubMessage_substuff2_default;
extern const uint32_t SubMessage_substuff3_default;
extern const int32_t AllTypes_opt_int32_default;
extern const int64_t AllTypes_opt_int64_default;
extern const uint32_t AllTypes_opt_uint32_default;
extern const uint64_t AllTypes_opt_uint64_default;
extern const int32_t AllTypes_opt_sint32_default;
extern const int64_t AllTypes_opt_sint64_default;
extern const bool AllTypes_opt_bool_default;
extern const uint32_t AllTypes_opt_fixed32_default;
extern const int32_t AllTypes_opt_sfixed32_default;
extern const float AllTypes_opt_float_default;
extern const uint64_t AllTypes_opt_fixed64_default;
extern const int64_t AllTypes_opt_sfixed64_default;
extern const double AllTypes_opt_double_default;
extern const char AllTypes_opt_string_default[16];
extern const AllTypes_opt_bytes_t AllTypes_opt_bytes_default;
extern const MyEnum AllTypes_opt_enum_default;
/* Struct field encoding specification for nanopb */
extern const pb_field_t SubMessage_fields[4];
extern const pb_field_t AllTypes_fields[53];
/* Check that field information fits in pb_field_t */
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 256 && pb_membersize(AllTypes, rep_submsg[0]) < 256 && pb_membersize(AllTypes, opt_submsg) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_SubMessage_AllTypes)
#endif
#if !defined(PB_FIELD_32BIT)
STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 65536 && pb_membersize(AllTypes, rep_submsg[0]) < 65536 && pb_membersize(AllTypes, opt_submsg) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_SubMessage_AllTypes)
#endif
/* On some platforms (such as AVR), double is really float.
* These are not directly supported by nanopb, but see example_avr_double.
*/
STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

197
tests/bc_decode.c Normal file
View File

@@ -0,0 +1,197 @@
/* Tests the decoding of all types.
* This is a backwards-compatibility test, using bc_alltypes.pb.h.
* It is similar to test_decode3, but duplicated in order to allow
* test_decode3 to test any new features introduced later.
*
* Run e.g. ./bc_encode | ./bc_decode
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pb_decode.h>
#include "bc_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, int mode)
{
AllTypes alltypes;
/* Fill with garbage to better detect initialization errors */
memset(&alltypes, 0xAA, sizeof(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_submsg.substuff3 == 3);
TEST(alltypes.req_enum == MyEnum_Truth);
TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0);
TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0);
TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0);
TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0);
TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0);
TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0);
TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false);
TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0);
TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0);
TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f);
TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0);
TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0);
TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0);
TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0');
TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0);
TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0);
TEST(alltypes.rep_submsg_count == 5);
TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0');
TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0);
TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 3);
TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero);
if (mode == 0)
{
/* Expect default values */
TEST(alltypes.has_opt_int32 == false);
TEST(alltypes.opt_int32 == 4041);
TEST(alltypes.has_opt_int64 == false);
TEST(alltypes.opt_int64 == 4042);
TEST(alltypes.has_opt_uint32 == false);
TEST(alltypes.opt_uint32 == 4043);
TEST(alltypes.has_opt_uint64 == false);
TEST(alltypes.opt_uint64 == 4044);
TEST(alltypes.has_opt_sint32 == false);
TEST(alltypes.opt_sint32 == 4045);
TEST(alltypes.has_opt_sint64 == false);
TEST(alltypes.opt_sint64 == 4046);
TEST(alltypes.has_opt_bool == false);
TEST(alltypes.opt_bool == false);
TEST(alltypes.has_opt_fixed32 == false);
TEST(alltypes.opt_fixed32 == 4048);
TEST(alltypes.has_opt_sfixed32 == false);
TEST(alltypes.opt_sfixed32 == 4049);
TEST(alltypes.has_opt_float == false);
TEST(alltypes.opt_float == 4050.0f);
TEST(alltypes.has_opt_fixed64 == false);
TEST(alltypes.opt_fixed64 == 4051);
TEST(alltypes.has_opt_sfixed64 == false);
TEST(alltypes.opt_sfixed64 == 4052);
TEST(alltypes.has_opt_double == false);
TEST(alltypes.opt_double == 4053.0);
TEST(alltypes.has_opt_string == false);
TEST(strcmp(alltypes.opt_string, "4054") == 0);
TEST(alltypes.has_opt_bytes == false);
TEST(alltypes.opt_bytes.size == 4);
TEST(memcmp(alltypes.opt_bytes.bytes, "4055", 4) == 0);
TEST(alltypes.has_opt_submsg == false);
TEST(strcmp(alltypes.opt_submsg.substuff1, "1") == 0);
TEST(alltypes.opt_submsg.substuff2 == 2);
TEST(alltypes.opt_submsg.substuff3 == 3);
TEST(alltypes.has_opt_enum == false);
TEST(alltypes.opt_enum == MyEnum_Second);
}
else
{
/* Expect filled-in values */
TEST(alltypes.has_opt_int32 == true);
TEST(alltypes.opt_int32 == 3041);
TEST(alltypes.has_opt_int64 == true);
TEST(alltypes.opt_int64 == 3042);
TEST(alltypes.has_opt_uint32 == true);
TEST(alltypes.opt_uint32 == 3043);
TEST(alltypes.has_opt_uint64 == true);
TEST(alltypes.opt_uint64 == 3044);
TEST(alltypes.has_opt_sint32 == true);
TEST(alltypes.opt_sint32 == 3045);
TEST(alltypes.has_opt_sint64 == true);
TEST(alltypes.opt_sint64 == 3046);
TEST(alltypes.has_opt_bool == true);
TEST(alltypes.opt_bool == true);
TEST(alltypes.has_opt_fixed32 == true);
TEST(alltypes.opt_fixed32 == 3048);
TEST(alltypes.has_opt_sfixed32 == true);
TEST(alltypes.opt_sfixed32 == 3049);
TEST(alltypes.has_opt_float == true);
TEST(alltypes.opt_float == 3050.0f);
TEST(alltypes.has_opt_fixed64 == true);
TEST(alltypes.opt_fixed64 == 3051);
TEST(alltypes.has_opt_sfixed64 == true);
TEST(alltypes.opt_sfixed64 == 3052);
TEST(alltypes.has_opt_double == true);
TEST(alltypes.opt_double == 3053.0);
TEST(alltypes.has_opt_string == true);
TEST(strcmp(alltypes.opt_string, "3054") == 0);
TEST(alltypes.has_opt_bytes == true);
TEST(alltypes.opt_bytes.size == 4);
TEST(memcmp(alltypes.opt_bytes.bytes, "3055", 4) == 0);
TEST(alltypes.has_opt_submsg == true);
TEST(strcmp(alltypes.opt_submsg.substuff1, "3056") == 0);
TEST(alltypes.opt_submsg.substuff2 == 3056);
TEST(alltypes.opt_submsg.substuff3 == 3);
TEST(alltypes.has_opt_enum == true);
TEST(alltypes.opt_enum == MyEnum_Truth);
}
TEST(alltypes.end == 1099);
return true;
}
int main(int argc, char **argv)
{
/* Whether to expect the optional values or the default values. */
int mode = (argc > 1) ? atoi(argv[1]) : 0;
/* Read the data into buffer */
uint8_t buffer[1024];
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, mode))
{
printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
return 1;
} else {
return 0;
}
}

131
tests/bc_encode.c Normal file
View File

@@ -0,0 +1,131 @@
/* Attempts to test all the datatypes supported by ProtoBuf.
* This is a backwards-compatibility test, using bc_alltypes.pb.h.
* It is similar to test_encode3, but duplicated in order to allow
* test_encode3 to test any new features introduced later.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pb_encode.h>
#include "bc_alltypes.pb.h"
int main(int argc, char **argv)
{
int mode = (argc > 1) ? atoi(argv[1]) : 0;
/* Initialize the structure with constants */
AllTypes alltypes = {0};
alltypes.req_int32 = -1001;
alltypes.req_int64 = -1002;
alltypes.req_uint32 = 1003;
alltypes.req_uint64 = 1004;
alltypes.req_sint32 = -1005;
alltypes.req_sint64 = -1006;
alltypes.req_bool = true;
alltypes.req_fixed32 = 1008;
alltypes.req_sfixed32 = -1009;
alltypes.req_float = 1010.0f;
alltypes.req_fixed64 = 1011;
alltypes.req_sfixed64 = -1012;
alltypes.req_double = 1013.0;
strcpy(alltypes.req_string, "1014");
alltypes.req_bytes.size = 4;
memcpy(alltypes.req_bytes.bytes, "1015", 4);
strcpy(alltypes.req_submsg.substuff1, "1016");
alltypes.req_submsg.substuff2 = 1016;
alltypes.req_enum = MyEnum_Truth;
alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001;
alltypes.rep_int64_count = 5; alltypes.rep_int64[4] = -2002;
alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003;
alltypes.rep_uint64_count = 5; alltypes.rep_uint64[4] = 2004;
alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005;
alltypes.rep_sint64_count = 5; alltypes.rep_sint64[4] = -2006;
alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true;
alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008;
alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009;
alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f;
alltypes.rep_fixed64_count = 5; alltypes.rep_fixed64[4] = 2011;
alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64[4] = -2012;
alltypes.rep_double_count = 5; alltypes.rep_double[4] = 2013.0;
alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014");
alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4;
memcpy(alltypes.rep_bytes[4].bytes, "2015", 4);
alltypes.rep_submsg_count = 5;
strcpy(alltypes.rep_submsg[4].substuff1, "2016");
alltypes.rep_submsg[4].substuff2 = 2016;
alltypes.rep_submsg[4].has_substuff3 = true;
alltypes.rep_submsg[4].substuff3 = 2016;
alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth;
if (mode != 0)
{
/* Fill in values for optional fields */
alltypes.has_opt_int32 = true;
alltypes.opt_int32 = 3041;
alltypes.has_opt_int64 = true;
alltypes.opt_int64 = 3042;
alltypes.has_opt_uint32 = true;
alltypes.opt_uint32 = 3043;
alltypes.has_opt_uint64 = true;
alltypes.opt_uint64 = 3044;
alltypes.has_opt_sint32 = true;
alltypes.opt_sint32 = 3045;
alltypes.has_opt_sint64 = true;
alltypes.opt_sint64 = 3046;
alltypes.has_opt_bool = true;
alltypes.opt_bool = true;
alltypes.has_opt_fixed32 = true;
alltypes.opt_fixed32 = 3048;
alltypes.has_opt_sfixed32 = true;
alltypes.opt_sfixed32 = 3049;
alltypes.has_opt_float = true;
alltypes.opt_float = 3050.0f;
alltypes.has_opt_fixed64 = true;
alltypes.opt_fixed64 = 3051;
alltypes.has_opt_sfixed64 = true;
alltypes.opt_sfixed64 = 3052;
alltypes.has_opt_double = true;
alltypes.opt_double = 3053.0;
alltypes.has_opt_string = true;
strcpy(alltypes.opt_string, "3054");
alltypes.has_opt_bytes = true;
alltypes.opt_bytes.size = 4;
memcpy(alltypes.opt_bytes.bytes, "3055", 4);
alltypes.has_opt_submsg = true;
strcpy(alltypes.opt_submsg.substuff1, "3056");
alltypes.opt_submsg.substuff2 = 3056;
alltypes.has_opt_enum = true;
alltypes.opt_enum = MyEnum_Truth;
}
alltypes.end = 1099;
uint8_t buffer[1024];
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
{
fprintf(stderr, "Encoding failed!\n");
return 1; /* Failure */
}
}

9
tests/callbacks2.proto Normal file
View File

@@ -0,0 +1,9 @@
// Test if including generated header file for this file + implicit include of
// callbacks.pb.h still compiles. Used with test_compiles.c.
import "callbacks.proto";
message Callback2Message {
required TestMessage tstmsg = 1;
required SubMessage submsg = 2;
}

View File

View File

@@ -2,4 +2,6 @@ char filesize\[20\];
char msgsize\[30\];
char fieldsize\[40\];
pb_callback_t int32_callback;
[^_]EnumValue1 = 1
\sEnumValue1 = 1
Message5_EnumValue1
} pb_packed my_packed_struct;

View File

@@ -36,4 +36,38 @@ enum Enum1
{
option (nanopb_enumopt).long_names = false;
EnumValue1 = 1;
EnumValue2 = 2;
}
message EnumTest
{
required Enum1 field = 1 [default = EnumValue2];
}
// Short enum names inside message
message Message5
{
enum Enum2
{
option (nanopb_enumopt).long_names = false;
EnumValue1 = 1;
}
required Enum2 field = 1 [default = EnumValue1];
}
// Packed structure
message my_packed_struct
{
option (nanopb_msgopt).packed_struct = true;
optional int32 myfield = 1;
}
// Message with ignored field
// Note: doesn't really test if the field is missing in the output,
// but atleast tests that the output compiles.
message Message6
{
required int32 field1 = 1;
optional int32 field2 = 2 [(nanopb).type = FT_IGNORE];
}

View File

@@ -0,0 +1,13 @@
/*
* Tests if still compile if typedefs are redfefined in STATIC_ASSERTS when
* proto file includes another poto file
*/
#include <stdio.h>
#include <pb_encode.h>
#include "callbacks2.pb.h"
int main()
{
return 0;
}