Compare commits

..

134 Commits

Author SHA1 Message Date
Petteri Aimonen
7a9c29f2d7 Perform field initialization to defaults only when the field is skipped.
Avoids unnecessary initialization & unnecessary scan of
the pb_field_t array.

Runtime on Cortex-M3 -5%, code size +2%.

Could need some more testing with random field orders.
Have to write a tool to randomize fields in a message.
2013-04-05 21:35:36 +03:00
Petteri Aimonen
384e686fe6 Do not generate has_ fields for callback fields.
The arg field can be used to store the field presence from inside
the callback. Furthermore, having the has_ field for encoding callbacks
would be more annoying than useful.

Update issue 70
Status: FixedInGit
2013-04-02 20:01:31 +03:00
Petteri Aimonen
214b0eae8a Change the callback function to use void**.
NOTE: This change breaks backwards-compatibility by default.
If you have old callback functions, you can define PB_OLD_CALLBACK_STYLE
to retain the old behaviour.

If you want to convert your old callbacks to new signature, you need
to do the following:

1) Change decode callback argument to   void **arg
      and encode callback argument to   void * const *arg.

2) Change any reference to arg into *arg.

The rationale for making the new behaviour the default is that it
simplifies the common case of "allocate some memory in decode callback".

Update issue 69
Status: FixedInGit
2013-04-02 19:55:21 +03:00
Petteri Aimonen
6f3740f74e Fix warning on clang.
Update issue 67
Status: FixedInGit
2013-03-13 15:34:12 +02:00
Petteri Aimonen
1396dce2ae Add support for packed structures on IAR and MSVC.
Update issue 66
Status: FixedInGit
2013-03-13 15:22:00 +02:00
Petteri Aimonen
6468f23d23 Make the generator options accept a file name in addition to format string. 2013-03-09 23:03:09 +02:00
Petteri Aimonen
03e5393072 Add PB_SYSTEM_HEADER compile time option.
This allows replacing the C99 standard include file names with
a single system-specific file. It should provide all the necessary
system functions (typedefs, memset, memcpy, strlen).

Update issue 62
Status: FixedInGit
2013-03-09 14:56:34 +02:00
Petteri Aimonen
d580b225e8 Rename pb_field_iterator_t field 'current' to 'pos'.
This avoids a name clash when compiling as Linux kernel module.

Update issue 60
Status: FixedInGit
2013-03-09 14:52:38 +02:00
Petteri Aimonen
0352647118 Implement error message support for the encoder side.
Update issue 7
Status: FixedInGit
2013-03-09 14:49:15 +02:00
Petteri Aimonen
9b6641ac64 alltypes.proto no longer needs to include nanopb.proto 2013-03-09 14:45:41 +02:00
Petteri Aimonen
5f3bf35e01 Switch alltypes.proto to use the new .options file mechanism. 2013-03-09 14:23:44 +02:00
Petteri Aimonen
6f8dbc73eb Add simple support for separate options file.
Update issue 12
Still needs documentation.
2013-03-09 14:21:21 +02:00
Petteri Aimonen
e1b8a555f3 Fix additional bug with empty message types.
pb_field_next() would access past the fields array.
2013-03-09 13:12:09 +02:00
Petteri Aimonen
60109c0be1 Add option to run the tests with mudflap to detect pointer errors. 2013-03-09 13:09:14 +02:00
Petteri Aimonen
96d4016838 Improve the fuzztest.
Enable -fstack-protector-all to detect any stack smashing bugs. Also
use test_decode3 for maximal vulnerable surface.
2013-03-09 12:51:47 +02:00
Petteri Aimonen
a9c88f5570 Fix error in backwards compatibility testcase 2013-03-09 12:43:35 +02:00
Petteri Aimonen
d2e3c1ad93 Fix bug with decoding empty message types. Add test for the same.
Note: the bug only applies to empty message types. Empty messages
of non-empty message types are not affected.

Update issue 65
Status: FixedInGit
2013-03-09 12:35:07 +02:00
Petteri Aimonen
5522e02133 Add a dummy field if struct would otherwise be empty.
Update issue 64
Status: FixedInGit
2013-03-06 18:02:57 +02:00
Petteri Aimonen
64bf72d73d Add generator option to configure #include directives.
This suits complex projects, where there are multiple interdependent .proto files
in various directories. Patch by Michael Haberler.
2013-03-04 19:27:42 +02:00
Petteri Aimonen
104710b26c Setting version to 0.2.1-dev 2013-03-02 16:35:17 +02:00
Petteri Aimonen
c8e0732e6b Publishing nanopb-0.2.0 2013-03-02 16:32:54 +02:00
Petteri Aimonen
86ae2541e6 Update changelog 2013-03-02 16:32:15 +02:00
Petteri Aimonen
f8a143fdfe Update documentation 2013-03-02 16:27:31 +02:00
Petteri Aimonen
0e3053894f Add --extension option to generator.
Patch courtesy of Michael Haberler.
2013-02-28 16:42:34 +02:00
Pavel Ilin
57e81ca73b Added CMake file for use in projects linking against nanopb. 2013-02-22 20:25:39 +02:00
Petteri Aimonen
5442e690f6 Merge branch 'dev-0.2' 2013-02-21 19:38:56 +02:00
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
5b536d40a1 Setting version to 0.1.9.1-dev 2013-02-13 21:13:35 +02:00
Petteri Aimonen
4cc3372b03 Publishing nanopb-0.1.9 2013-02-13 21:12:09 +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
Petteri Aimonen
92bb37b074 Publishing nanopb-0.1.7 2012-11-11 11:16:01 +02:00
Petteri Aimonen
ad9a885644 Document new generator options 2012-10-29 19:33:33 +02:00
Petteri Aimonen
08391f35ee Add nanopb version number to generated files.
tools/set_version.sh is used to update the values.

Update issue 36
Status: FixedInGit
2012-10-29 19:15:34 +02:00
Petteri Aimonen
3aaa4ad8c2 Add extern "C" to header files.
Update issue 35
Status: FixedInGit
2012-10-29 19:03:19 +02:00
Petteri Aimonen
db1eefc24b Add option to use short names for enum values.
Update issue 38
Status: FixedInGit
2012-10-29 18:55:49 +02:00
Petteri Aimonen
0ee4bb96b1 Allow defining field type in .proto.
Update issue 30
Status: FixedInGit
2012-10-29 18:34:24 +02:00
Petteri Aimonen
28b0136ea4 Improve .proto options parsing.
Options can now be defined on command line, file, message or in field
scope.

Update issue 12
Status: Started
2012-10-29 18:20:15 +02:00
Petteri Aimonen
9e0ee92f0a Use optparse in nanopb_generator.py 2012-10-29 17:25:16 +02:00
Petteri Aimonen
c3fa362653 Give names to generated structures to allow forward declaration.
Update issue 39
Status: FixedInGit
2012-10-29 16:56:45 +02:00
Petteri Aimonen
bffd3a9acc Improve the person_with_extra_field test.
Added a field after the extra field to verify it's also ok.
2012-10-21 21:31:20 +03:00
Petteri Aimonen
dcab39a41c Remove the "buf = NULL" => skip requirement from pb_istream_t callbacks.
Rationale: it's easy to implement the callback wrong. Doing so introduces
io errors when unknown fields are present in the input. If code is not
tested with unknown fields, these bugs can remain hidden for long time.

Added a special case for the memory buffer stream, where it gives a small
speed benefit.

Added testcase for skipping fields with test_decode2 implementation.

Update issue 37
Status: FixedInGit
2012-10-18 19:45:28 +03:00
Petteri Aimonen
900c8dd125 Add tests for negative values in the alltypes testcase. 2012-10-14 12:27:08 +03:00
Petteri Aimonen
3f563792ad Add buffer size check in test_decode1.c example.
This check gives a better error message in case you test stuff
and have a message longer than 512 bytes.

Update issue 34
Status: FixedInGit
2012-09-18 16:51:22 +03:00
Petteri Aimonen
b214de4e1e Enable -Wconversion for core and fix the warnings.
This should help avoid issue 33 in the future.
2012-09-03 17:35:14 +03:00
dch
be78e3b4d0 Fix build warnings on MS compilers
Update issue 33
Status: FixedInGit
2012-09-03 17:18:49 +03:00
Petteri Aimonen
11e0918b5e Created a CHANGELOG file for easy reference. 2012-09-02 20:49:08 +03:00
Petteri Aimonen
07d44cf632 Clean up one (uint64_t) cast. 2012-09-02 20:14:22 +03:00
Petteri Aimonen
f187aad63d Fix -Wextra warning in generated .pb.c files.
Compile the generated files with the same extra-strict
settings as the core, in order to detect problems in tests.

Update issue 32
Status: FixedInGit
2012-09-02 19:54:35 +03:00
Petteri Aimonen
a06dba6e49 Fix warnings with -Wcast-qual. Add test for C++ compile.
Update issue 27
Status: FixedInGit
2012-08-26 15:21:20 +03:00
Petteri Aimonen
160f02e4d0 Add pb_decode_noinit and use it from pb_dec_submessage.
This avoids double initialization when decoding nested submessages.
Fixes an issue with submessage arrays that was present in previous
version of this patch.

Update issue 28
Status: FixedInGit
2012-08-26 11:04:55 +03:00
Petteri Aimonen
a7a6cdad88 Added alltypes-testcases for optional fields and default values. 2012-08-26 10:57:51 +03:00
Petteri Aimonen
1463e687e3 Revert "Add pb_decode_noinit and use it from pb_dec_submessage."
The patch breaks default values inside submessage arrays (I think).
Have to add test cases and check back.

This reverts commit f1d7640fe1.
2012-08-26 10:05:46 +03:00
Petteri Aimonen
f1d7640fe1 Add pb_decode_noinit and use it from pb_dec_submessage.
This avoids double initialization when decoding nested submessages.

Update issue 28
Status: FixedInGit
2012-08-26 09:56:11 +03:00
Petteri Aimonen
440da6959f Add note that stream callbacks must read the whole requested length.
Fixes issue #22.
2012-08-24 21:32:42 +03:00
Petteri Aimonen
0fb5e5e068 Implement error messages in the decoder side.
Update issue 7
Status: Started
2012-08-24 21:22:20 +03:00
Petteri Aimonen
ea57f74741 Document pb_close_string_substream. 2012-08-24 20:51:29 +03:00
Petteri Aimonen
9383f305dc Revert "Change the substream implementation in pb_decode."
This reverts commit dc2da0edc5.

Add pb_close_string_substream() for copying back the state.
This makes adding error messages easier in the future, as also
them need to be propagated back from the substream.
2012-08-24 20:43:39 +03:00
Petteri Aimonen
2941e90e04 Reorganize the field decoder interface.
This makes the field decoding functions more intuitive to use.
The old interface is still present if you specify NANOPB_INTERNALS.

Update issue 2
Status: FixedInGit
2012-08-24 20:23:25 +03:00
Petteri Aimonen
dc2da0edc5 Change the substream implementation in pb_decode.
This makes it unnecessary to copy back the state, and also relaxes
the requirements on callbacks (bytes_left will always be valid).
It decreases code size by a few bytes, but may be just slightly slower.
2012-08-24 19:35:17 +03:00
Petteri Aimonen
86257a2a70 Fix a confusing statement in a comment. 2012-08-24 19:25:29 +03:00
Stan Hu
07f24f9b70 Fix casting issues 2012-08-16 22:06:19 +03:00
Petteri Aimonen
8d5086f052 Declare warn_unused_result only on GCC >= 3.4.0.
Thanks to cea.max.simoes for bug report & fix.

Update issue 25
Status: FixedInGit
2012-08-09 16:15:23 +03:00
Petteri Aimonen
1aa61f108a Fix signedness warning in example_unions. 2012-08-04 14:34:19 +03:00
Petteri Aimonen
b582bc9bf6 Fix bug in decoder with packed arrays.
Update issue 23
Status: FixedInGit
2012-07-31 19:12:57 +03:00
Petteri Aimonen
5703ad0c55 Extend 'alltypes' testcase to cover repeated fields. 2012-07-31 19:10:33 +03:00
Petteri Aimonen
0a5b6852ec Additional unsigned vs. signed fix for tag 2012-07-19 09:05:36 +03:00
Petteri Aimonen
8524de39ce Fix an unsigned vs. signed warning on some compiler. 2012-07-18 21:21:07 +03:00
Petteri Aimonen
10b5da12dc Fixed a few compiler warnings, added check.
Main code is now compiled (for tests) with -pedantic -Wextra.
The test programs are not as strictly bound, but this should
improve the chances that atleast the core library compiles with
most compilers without warnings.
2012-07-18 21:09:13 +03:00
Petteri Aimonen
d8bddabb83 Fix bug with .proto without messages (again), and add a test case for it. 2012-07-05 20:02:06 +03:00
Petteri Aimonen
01a1556898 Apparently some compilers don't want to automatically cast size_t to uint64_t. 2012-07-05 18:27:07 +03:00
Petteri Aimonen
efef38cf78 Fix some typos in __BIG_ENDIAN__ code 2012-07-05 18:24:11 +03:00
Petteri Aimonen
72cca8d783 Replace #warning with the standard #error. 2012-07-05 18:19:38 +03:00
Stan Hu
7c5bb6541a Cast enum vaules to integers and cast them back where appropraite
to prevent mixed enumeration type compiler warnings
2012-07-05 18:13:50 +03:00
Petteri Aimonen
9b1e1b440a Replace PB_MANY_FIELDS with PB_FIELD_16BIT and PB_FIELD_32BIT.
This allows more precise control over the memory use vs. field size.
2012-07-01 10:15:37 +03:00
Petteri Aimonen
78086cc27d Add PB_MANY_FIELDS option for supporting fields > 255.
Add generator warning if this is necessary.
Fixes issue #14.
2012-06-30 19:28:49 +03:00
Petteri Aimonen
67add3259a Warn if PB_MAX_REQUIRED_FIELDS is not large enough. 2012-06-30 18:23:18 +03:00
Petteri Aimonen
95eb4a5499 Improve the detection of missing required fields.
Now the limit of tracked fields is configurable at compile-time using
PB_MAX_REQUIRED_FIELDS. Added related test and updated documentation.

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

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

Fixes Issue 6.
2012-03-01 13:46:52 +02:00
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
62 changed files with 4399 additions and 2088 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

77
CHANGELOG Normal file
View File

@@ -0,0 +1,77 @@
nanopb-0.2.0
NOTE: This release requires you to regenerate all .pb.c
files. Files generated by older versions will not
compile anymore.
Reformat generated .pb.c files using macros (issue 58)
Rename PB_HTYPE_ARRAY -> PB_HTYPE_REPEATED
Separate PB_HTYPE to PB_ATYPE and PB_HTYPE
Move STATIC_ASSERTs to .pb.c file
Added CMake file (by Pavel Ilin)
Add option to give file extension to generator (by Michael Haberler)
Documentation updates
nanopb-0.1.9
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)
Add nanopb version number to generated files (issue 36)
Add extern "C" to generated headers (issue 35)
Add names for structs to allow forward declaration (issue 39)
Add buffer size check in example (issue 34)
Fix build warnings on MS compilers (issue 33)
nanopb-0.1.6
Reorganize the field decoder interface (issue 2)
Improve performance in submessage decoding (issue 28)
Implement error messages in the decoder side (issue 7)
Extended testcases (alltypes test is now complete).
Fix some compiler warnings (issues 25, 26, 27, 32).
nanopb-0.1.5
Fix bug in decoder with packed arrays (issue 23).
Extended testcases.
Fix some compiler warnings.
nanopb-0.1.4
Add compile-time options for easy-to-use >255 field support.
Improve the detection of missing required fields.
Added example on how to handle union messages.
Fix generator error with .proto without messages.
Fix problems that stopped the code from compiling with some compilers.
Fix some compiler warnings.
nanopb-0.1.3
Refactor the field encoder interface.
Improve generator error messages (issue 5)
Add descriptor.proto into the #include exclusion list
Fix some compiler warnings.
nanopb-0.1.2
Make the generator to generate include for other .proto files (issue 4).
Fixed generator not working on Windows (issue 3)
nanopb-0.1.1
Fixed bug in encoder with 'bytes' fields (issue 1).
Fixed a bug in the generator that caused a compiler error on sfixed32 and sfixed64 fields.
Extended testcases.
nanopb-0.1.0
First stable release.

View File

@@ -1,5 +1,4 @@
Copyright (c) 2011 Petteri Aimonen <jpa at nanopb.mail.kapsi.fi>
Copyright (c) 2011 Michael Poole <mdpoole@troilus.org>
This software is provided 'as-is', without any express or
implied warranty. In no event will the authors be held liable

2
README
View File

@@ -5,5 +5,7 @@ Homepage: http://kapsi.fi/~jpa/nanopb/
To compile the library, you'll need these libraries:
protobuf-compiler python-protobuf libprotobuf-dev
The only runtime dependencies are memset() and memcpy().
To run the tests, run make under the tests folder.
If it completes without error, everything is fine.

224
cmake/FindNanopb.cmake Normal file
View File

@@ -0,0 +1,224 @@
# Locate and configure the nanopb library.
#
# The following varialbes have to be set:
#
# NANOPB_SRC_ROOT_FOLDER - Path to nanopb source folder
#
# The following variables can be set and are optional:
#
#
# PROTOBUF_SRC_ROOT_FOLDER - When compiling with MSVC, if this cache variable is set
# the protobuf-default VS project build locations
# (vsprojects/Debug & vsprojects/Release) will be searched
# for libraries and binaries.
#
# NANOPB_IMPORT_DIRS - List of additional directories to be searched for
# imported .proto files.
#
# NANOPB_GENERATE_CPP_APPEND_PATH - By default -I will be passed to protoc
# for each directory where a proto file is referenced.
# Set to FALSE if you want to disable this behaviour.
#
# Defines the following variables:
#
# NANOPB_FOUND - Found the nanopb library (source&header files, generator tool, protoc compiler tool)
# NANOPB_INCLUDE_DIRS - Include directories for Google Protocol Buffers
#
# The following cache variables are also available to set or use:
# NANOPB_GENERATOR_EXECUTABLE - The nanopb generator
# PROTOBUF_PROTOC_EXECUTABLE - The protoc compiler
#
# ====================================================================
#
# NANOPB_GENERATE_CPP (public function)
# SRCS = Variable to define with autogenerated
# source files
# HDRS = Variable to define with autogenerated
# header files
# ARGN = proto files
#
# ====================================================================
# Example:
#
# set(NANOPB_SRC_ROOT_FOLDER "/path/to/nanopb")
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${NANOPB_SRC_ROOT_FOLDER}/cmake)
# find_package( Nanopb REQUIRED )
# include_directories(${NANOPB_INCLUDE_DIRS})
#
# NANOPB_GENERATE_CPP(PROTO_SRCS PROTO_HDRS foo.proto)
#
# include_directories(${CMAKE_CURRENT_BINARY_DIR})
# add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})
#
# ====================================================================
#=============================================================================
# Copyright 2009 Kitware, Inc.
# Copyright 2009-2011 Philip Lowman <philip@yhbt.com>
# Copyright 2008 Esben Mose Hansen, Ange Optimization ApS
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
# nor the names of their contributors may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#=============================================================================
#
# Changes
# 2013.01.31 - Pavlo Ilin - used Modules/FindProtobuf.cmake from cmake 2.8.10 to
# write FindNanopb.cmake
#
#=============================================================================
function(NANOPB_GENERATE_CPP SRCS HDRS)
if(NOT ARGN)
return()
endif()
if(NANOPB_GENERATE_CPP_APPEND_PATH)
# Create an include path for each file specified
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(ABS_PATH ${ABS_FIL} PATH)
list(FIND _nanobp_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _nanobp_include_path -I ${ABS_PATH})
endif()
endforeach()
else()
set(_nanobp_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(DEFINED NANOPB_IMPORT_DIRS)
foreach(DIR ${NANOPB_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _nanobp_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _nanobp_include_path -I ${ABS_PATH})
endif()
endforeach()
endif()
set(${SRCS})
set(${HDRS})
get_filename_component(GENERATOR_PATH ${NANOPB_GENERATOR_EXECUTABLE} PATH)
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.c")
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb"
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
ARGS -I${GENERATOR_PATH} -I${CMAKE_CURRENT_BINARY_DIR} ${_nanobp_include_path} -o${FIL_WE}.pb ${ABS_FIL}
DEPENDS ${ABS_FIL}
COMMENT "Running C++ protocol buffer compiler on ${FIL}"
VERBATIM )
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.c"
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
COMMAND python
ARGS ${NANOPB_GENERATOR_EXECUTABLE} ${FIL_WE}.pb
DEPENDS ${FIL_WE}.pb
COMMENT "Running nanopb generator on ${FIL_WE}.pb"
VERBATIM )
endforeach()
set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
set(${SRCS} ${${SRCS}} ${NANOPB_SRCS} PARENT_SCOPE)
set(${HDRS} ${${HDRS}} ${NANOPB_HDRS} PARENT_SCOPE)
endfunction()
#
# Main.
#
# By default have NANOPB_GENERATE_CPP macro pass -I to protoc
# for each directory where a proto file is referenced.
if(NOT DEFINED NANOPB_GENERATE_CPP_APPEND_PATH)
set(NANOPB_GENERATE_CPP_APPEND_PATH TRUE)
endif()
# Find the include directory
find_path(NANOPB_INCLUDE_DIRS
pb.h
PATHS ${NANOPB_SRC_ROOT_FOLDER}
)
mark_as_advanced(NANOPB_INCLUDE_DIRS)
# Find nanopb source files
set(NANOPB_SRCS)
set(NANOPB_HDRS)
list(APPEND _nanopb_srcs pb_decode.c pb_encode.c)
list(APPEND _nanopb_hdrs pb_decode.h pb_encode.h pb.h)
foreach(FIL ${_nanopb_srcs})
find_file(${FIL}__nano_pb_file NAMES ${FIL} PATHS ${NANOPB_SRC_ROOT_FOLDER} ${NANOPB_INCLUDE_DIRS})
list(APPEND NANOPB_SRCS "${${FIL}__nano_pb_file}")
mark_as_advanced(${FIL}__nano_pb_file)
endforeach()
foreach(FIL ${_nanopb_hdrs})
find_file(${FIL}__nano_pb_file NAMES ${FIL} PATHS ${NANOPB_INCLUDE_DIRS})
mark_as_advanced(${FIL}__nano_pb_file)
list(APPEND NANOPB_HDRS "${${FIL}__nano_pb_file}")
endforeach()
# Find the protoc Executable
find_program(PROTOBUF_PROTOC_EXECUTABLE
NAMES protoc
DOC "The Google Protocol Buffers Compiler"
PATHS
${PROTOBUF_SRC_ROOT_FOLDER}/vsprojects/Release
${PROTOBUF_SRC_ROOT_FOLDER}/vsprojects/Debug
)
mark_as_advanced(PROTOBUF_PROTOC_EXECUTABLE)
# Find nanopb generator
find_file(NANOPB_GENERATOR_EXECUTABLE
NAMES nanopb_generator.py
DOC "nanopb generator"
PATHS
${NANOPB_SRC_ROOT_FOLDER}/generator
)
mark_as_advanced(NANOPB_GENERATOR_EXECUTABLE)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(NANOPB DEFAULT_MSG
NANOPB_INCLUDE_DIRS
NANOPB_SRCS NANOPB_HDRS
NANOPB_GENERATOR_EXECUTABLE
PROTOBUF_PROTOC_EXECUTABLE
)

View File

@@ -38,6 +38,20 @@ This file, in turn, requires the file *google/protobuf/descriptor.proto*. This i
protoc -I/usr/include -Inanopb/generator -I. -omessage.pb message.proto
The options can be defined in file, message and field scopes::
option (nanopb_fileopt).max_size = 20; // File scope
message Message
{
option (nanopb_msgopt).max_size = 30; // Message scope
required string fieldsize = 1 [(nanopb).max_size = 40]; // Field scope
}
It is also possible to give the options on command line, but then they will affect the whole file. For example::
user@host:~$ python ../generator/nanopb_generator.py -s 'max_size: 20' message.pb
Streams
=======
@@ -50,6 +64,7 @@ There are a few generic rules for callback functions:
#) Use state to store your own data, such as a file descriptor.
#) *bytes_written* and *bytes_left* are updated by pb_write and pb_read.
#) Your callback may be used with substreams. In this case *bytes_left*, *bytes_written* and *max_size* have smaller values than the original stream. Don't use these values to calculate pointers.
#) Always read or write the full requested length of data. For example, POSIX *recv()* needs the *MSG_WAITALL* parameter to accomplish this.
Output streams
--------------
@@ -74,7 +89,7 @@ This is the way to get the size of the message without storing it anywhere::
Person myperson = ...;
pb_ostream_t sizestream = {0};
pb_encode(&sizestream, Person_msg, &myperson);
pb_encode(&sizestream, Person_fields, &myperson);
printf("Encoded size is %d\n", sizestream.bytes_written);
**Example 2:**
@@ -91,9 +106,8 @@ Writing to stdout::
Input streams
-------------
For input streams, there are a few extra rules:
For input streams, there is one extra rule:
#) If buf is NULL, read from stream but don't store the data. This is used to skip unknown input.
#) You don't need to know the length of the message in advance. After getting EOF error when reading, set bytes_left to 0 and return false. Pb_decode will detect this and if the EOF was in a proper position, it will return true.
Here is the structure::
@@ -142,7 +156,6 @@ Most Protocol Buffers datatypes have directly corresponding C datatypes, such as
1) Strings, bytes and repeated fields of any type map to callback functions by default.
2) If there is a special option *(nanopb).max_size* specified in the .proto file, string maps to null-terminated char array and bytes map to a structure containing a char array and a size field.
3) If there is a special option *(nanopb).max_count* specified on a repeated field, it maps to an array of whatever type is being repeated. Another field will be created for the actual number of entries stored.
4) The option *(nanopb).pointer* can override the default (false, unless the *-p* option is passed to *nanopb_generator.py*) behavior. If a string, byte, or submessage is generated as a pointer field, and it is repeated (with a maximum count), required, or optional, the members will be pointers rather than in-line data.
=============================================================================== =======================
field in .proto autogenerated in .h
@@ -157,24 +170,20 @@ required bytes data = 1 [(nanopb).max_size = 40];
| uint8_t bytes[40];
| } Person_data_t;
| Person_data_t data;
required string name = 1 [(nanopb).pointer = true]; char \*name;
required bytes data = 1 [(nanopb).pointer = true]; pb_bytes_t data;
required bytes data = 1 [(nanopb).pointer = true, (nanopb).max_count = 5]; | size_t data_count;
| pb_bytes_t data[5];
required Message msg = 1 [(nanopb).pointer = true]; Message \*msg;
=============================================================================== =======================
The maximum lengths are checked in runtime. If string/bytes/array exceeds the allocated length, *pb_decode* will return false.
If a pointer-type field is not received, the field will be marked as absent, but the pointer will not be modified. This helps reduce memory fragmentation and churn, but increases worst-case memory usage and means you must use the *Message_has(object, Field)* macro rather than testing for a null pointer. You can use the `pb_clean`_ function to release unused memory in these cases.
.. _`pb_clean`: reference.html#pb-clean
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
===============
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.
The `pb_callback_t`_ structure contains a function pointer and a *void* pointer you can use for passing data to the callback. If the function pointer is NULL, the field will be skipped. The actual behavior of the callback function is different in encoding and decoding modes.
The `pb_callback_t`_ structure contains a function pointer and a *void* pointer called *arg* you can use for passing data to the callback. If the function pointer is NULL, the field will be skipped. A pointer to the *arg* is passed to the function, so that it can modify it and retrieve the value.
The actual behavior of the callback function is different in encoding and decoding modes. In encoding mode, the callback is called once and should write out everything, including field tags. In decoding mode, the callback is called repeatedly for every data item.
.. _`pb_callback_t`: reference.html#pb-callback-t
@@ -182,25 +191,21 @@ Encoding callbacks
------------------
::
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg);
bool (*encode_buffer)(pb_strstream_t *stream, const pb_field_t *field, const void *arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
When encoding, the callbacks should write out complete fields, including the wire type and field number tag. The callback can write as many or as few fields as it likes. For example, if you want to write out an array as *repeated* field, you should do it all in a single call.
When encoding, the callback should write out complete fields, including the wire type and field number tag. It can write as many or as few fields as it likes. For example, if you want to write out an array as *repeated* field, you should do it all in a single call.
Usually you can use `pb_encode_tag_for_field`_ (or `pb_encbuf_tag_for_field`_ for the *encode_buffer* callback) to encode the wire type and tag number of the field. However, if you want to encode a repeated field as a packed array, you must call `pb_encode_tag`_ (respectively, `pb_encbuf_tag`_) instead to specify a wire type of *PB_WT_STRING*.
Usually you can use `pb_encode_tag_for_field`_ to encode the wire type and tag number of the field. However, if you want to encode a repeated field as a packed array, you must call `pb_encode_tag`_ instead to specify a wire type of *PB_WT_STRING*.
If the callback is used in a submessage, *encode* will be called multiple times during a single call to `pb_encode`_. In this case, it must produce the same amount of data every time. If the callback is directly in the main message, or if you are using `pb_encode_buffer`_, the callback is called only once.
If the callback is used in a submessage, it will be called multiple times during a single call to `pb_encode`_. In this case, it must produce the same amount of data every time. If the callback is directly in the main message, it is called only once.
.. _`pb_encode`: reference.html#pb-encode
.. _`pb_encode_buffer`: reference.html#pb-encode-buffer
.. _`pb_encode_tag_for_field`: reference.html#pb-encode-tag-for-field
.. _`pb_encbuf_tag_for_field`: reference.html#pb-encbuf-tag-for-field
.. _`pb_encode_tag`: reference.html#pb-encode-tag
.. _`pb_encbuf_tag`: reference.html#pb-encbuf-tag
This callback writes out a dynamically sized string::
bool write_string(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
char *str = get_string_from_somewhere();
if (!pb_encode_tag_for_field(stream, field))
@@ -209,22 +214,11 @@ This callback writes out a dynamically sized string::
return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}
The equivalent for in-memory buffers has to write the elements in the opposite order, because the buffer writers prepend their data::
bool write_string_buf(pb_strstream_t *stream, const pb_field_t *field, const void *arg)
{
char *str = get_string_from_somewhere();
if (!pb_encbuf_string(stream, (uint8_t*)str, strlen(str)))
return false;
return pb_encbuf_tag_for_field(stream, field));
}
Decoding callbacks
------------------
::
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg);
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
When decoding, the callback receives a length-limited substring that reads the contents of a single field. The field tag has already been read. For *string* and *bytes*, the length value has already been parsed, and is available at *stream->bytes_left*.
@@ -234,7 +228,7 @@ The callback will be called multiple times for repeated fields. For packed field
This callback reads multiple integers and prints them::
bool read_ints(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool read_ints(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
while (stream->bytes_left)
{
@@ -246,10 +240,10 @@ This callback reads multiple integers and prints them::
return true;
}
Message descriptor
==================
Field description array
=======================
For using the *pb_encode*, *pb_encode_buffer* and *pb_decode* functions, you need a message descriptor describing the structure you wish to encode. This description is usually autogenerated from .proto file.
For using the *pb_encode* and *pb_decode* functions, you need an array of pb_field_t constants describing the structure you wish to encode. This description is usually autogenerated from .proto file.
For example this submessage in the Person.proto file::
@@ -260,67 +254,26 @@ For example this submessage in the Person.proto file::
}
}
generates these declarations and definitions for the structure *Person_PhoneNumber*::
generates this field description array for the structure *Person_PhoneNumber*::
typedef PB_MSG_STRUCT(2) Person_PhoneNumber_msg_t;
extern const Person_PhoneNumber_msg_t Person_PhoneNumber_real_msg;
#define Person_PhoneNumber_msg ((const pb_message_t*)&Person_PhoneNumber_real_msg)
const Person_PhoneNumber_msg_t Person_PhoneNumber_real_msg = {
2,
{
{1, PB_HTYPE_REQUIRED | PB_LTYPE_STRING,
offsetof(Person_PhoneNumber, number), 0,
pb_membersize(Person_PhoneNumber, number), 0, 0},
{2, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT,
pb_delta_end(Person_PhoneNumber, type, number), 0,
pb_membersize(Person_PhoneNumber, type), 0,
&Person_PhoneNumber_type_default},
}
const pb_field_t Person_PhoneNumber_fields[3] = {
PB_FIELD( 1, STRING , REQUIRED, STATIC, Person_PhoneNumber, number, number, 0),
PB_FIELD( 2, ENUM , OPTIONAL, STATIC, Person_PhoneNumber, type, number, &Person_PhoneNumber_type_default),
PB_LAST_FIELD
};
#define Person_PhoneNumber_has(STRUCT, FIELD) PB_HAS_FIELD(STRUCT, Person_PhoneNumber, FIELD)
#define Person_PhoneNumber_set(STRUCT, FIELD) PB_SET_FIELD(STRUCT, Person_PhoneNumber, FIELD)
#define Person_PhoneNumber_clear(STRUCT, FIELD) PB_CLEAR_FIELD(STRUCT, Person_PhoneNumber, FIELD)
#define Person_PhoneNumber_number_index 0
#define Person_PhoneNumber_number_tag 1
#define Person_PhoneNumber_type_index 1
#define Person_PhoneNumber_type_tag 2
Optional Fields
===============
The *has_fields* member of each generated structure is an array where
each bit indicates the presence of the corresponding (optional) field.
The generated header file provides helper macros to read and update
that array; in the previous example, they are
*Person_PhoneNumber_has*, *Person_PhoneNumber_set* and
*Person_PhoneNumber_clear*.
For convenience, *pb_encode* and *pb_encode_buffer* only check these
bits for optional fields. *pb_decode* sets the corresponding bit for
every field it decodes, whether the field is optional or not.
.. Should there be a section here on pointer fields?
Return values and error handling
================================
Most functions in nanopb return bool: *true* means success, *false* means failure. If this is enough for you, skip this section.
Most functions in nanopb return bool: *true* means success, *false* means failure. There is also some support for error messages for debugging purposes: the error messages go in *stream->errmsg*.
For simplicity, nanopb doesn't define it's own error codes. This might be added if there is a compelling need for it. You can however deduce something about the error causes:
The error messages help in guessing what is the underlying cause of the error. The most common error conditions are:
1) Running out of memory. Because everything is allocated from the stack, nanopb can't detect this itself. Encoding or decoding the same type of a message always takes the same amount of stack space. Therefore, if it works once, it works always.
2) Invalid field description. These are usually stored as constants, so if it works under the debugger, it always does.
3) IO errors in your own stream callbacks. Because encoding/decoding stops at the first error, you can overwrite the *state* field in the struct and store your own error code there.
4) Errors that happen in your callback functions. You can use the state field in the callback structure.
1) Running out of memory, i.e. stack overflow.
2) Invalid field descriptors (would usually mean a bug in the generator).
3) IO errors in your own stream callbacks.
4) Errors that happen in your callback functions.
5) Exceeding the max_size or bytes_left of a stream.
6) Exceeding the max_size of a string or array field
7) Invalid protocol buffers binary message. It's not like you could recover from it anyway, so a simple failure should be enough.
In my opinion, it is enough that 1. and 2. can be resolved using a debugger.
However, you may be interested which of the remaining conditions caused the error. For 3. and 4., you can set and check the state. If you have to detect 5. and 6., you should convert the fields to callback type. Any remaining problem is of type 7.
7) Invalid protocol buffers binary message.

View File

@@ -14,9 +14,6 @@ Overall structure
For the runtime program, you always need *pb.h* for type declarations.
Depending on whether you want to encode, decode, or both, you also need *pb_encode.h/c* or *pb_decode.h/c*.
If you only encode into in-memory buffers, *pb_decode_buffer.h/c* should be slightly faster and smaller.
If your *.proto* file encodes submessages or other fields using pointers, you must compile *pb_decode.c* with a preprocessor macro named *MALLOC_HEADER* that is the name of a header with definitions (either as functions or macros) for *calloc()*, *realloc()* and *free()*. For a typical hosted configuration, this should be *<stdlib.h>*.
The high-level encoding and decoding functions take an array of *pb_field_t* structures, which describes the fields of a message structure. Usually you want these autogenerated from a *.proto* file. The tool script *nanopb_generator.py* accomplishes this.
@@ -28,10 +25,9 @@ So a typical project might include these files:
- pb.h
- pb_decode.h and pb_decode.c (needed for decoding messages)
- pb_encode.h and pb_encode.c (needed for encoding messages)
- pb_encode_buffer.h and pb_encode_buffer.c (for encoding specifically into in-memory buffers)
2) Protocol description (you can have many):
- person.proto (just an example)
- person.pb.c (autogenerated, contains initializers for message descriptors)
- person.pb.c (autogenerated, contains initializers for const arrays)
- person.pb.h (autogenerated, contains type declarations)
Features and limitations
@@ -40,23 +36,26 @@ Features and limitations
**Features**
#) Pure C runtime
#) Small code size (210 kB depending on processor)
#) Small ram usage (typically 200 bytes)
#) Small code size (210 kB depending on processor, plus any message definitions)
#) Small ram usage (typically ~300 bytes, plus any message structs)
#) Allows specifying maximum size for strings and arrays, so that they can be allocated statically.
#) No malloc needed: everything can be allocated statically or on the stack.
#) You can use either encoder or decoder alone to cut the code size in half.
#) Support for most protobuf features, including: all data types, nested submessages, default values, repeated and optional fields, packed arrays.
#) Callback mechanism for handling messages larger than can fit in available RAM.
#) Extensive set of tests.
**Limitations**
#) User must provide callbacks when decoding arrays or strings without maximum size. Malloc support could be added as a separate module.
#) Some speed has been sacrificed for code size. For example varint calculations are always done in 64 bits.
#) Some speed has been sacrificed for code size.
#) Encoding is focused on writing to streams. For memory buffers only it could be made more efficient.
#) The deprecated Protocol Buffers feature called "groups" is not supported.
#) Fields in the generated structs are ordered by the tag number, instead of the natural ordering in .proto file.
#) Unknown fields are not preserved when decoding and re-encoding a message.
#) Reflection (runtime introspection) is not supported. E.g. you can't request a field by giving its name in a string.
#) Numeric arrays are always encoded as packed, even if not marked as packed in .proto. This causes incompatibility with decoders that do not support packed format.
#) Limited support for cyclic references between messages. (The cycle must be broken by making one of the references a pointer or callback field, and objects that have circular references are not detected when encoding.)
#) Cyclic references between messages are supported only in callback mode.
Getting started
===============
@@ -78,30 +77,17 @@ You should now have in *message.pb.h*::
int32_t value;
} Example;
typedef PB_MSG_STRUCT(1) Example_msg_t;
extern const Example_msg_t Example_real_msg;
#define Example_msg ((const pb_message_t*)&Example_real_msg)
extern const pb_field_t Example_fields[2];
Now in your main program do this to encode a message::
Example mymessage = {42};
uint8_t buffer[10];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
pb_encode(&stream, Example_msg, &mymessage);
pb_encode(&stream, Example_fields, &mymessage);
After that, buffer will contain the encoded message.
The number of bytes in the message is stored in *stream.bytes_written*.
Using *pb_encode_buffer.h/c* interface is very similar::
Example mymessage = {42};
uint8_t buffer[10];
pb_strstream_t stream = pb_str_from_buffer(buffer, sizeof(buffer));
pb_encode_buffer(&stream, Example_msg, &mymessage);
The encoded message will start at *stream.last* and continue until the
end of *buffer* (that is, it has length *buffer - stream.last*).
You can feed the message to *protoc --decode=Example message.proto* to verify its validity.
For complete examples of the simple cases, see *tests/test_decode1.c* and *tests/test_encode1.c*. For an example with network interface, see the *example* subdirectory.
@@ -121,9 +107,3 @@ Debugging and testing
=====================
Extensive unittests are included under the *tests* folder. Just type *make* there to run the tests.
This also generates a file called *breakpoints* which includes all lines returning *false* in nanopb. You can use this in gdb by typing *source breakpoints*, after which gdb will break on first nanopb error.
Wishlist
========
#) A cleaner rewrite of the Python-based source generator.
#) Better performance for 16- and 8-bit platforms: use smaller datatypes where possible.

View File

@@ -4,10 +4,6 @@
2) `Concepts`_
3) `API reference`_
Note: This is the documentation for the *dynamic_alloc_dev* development branch. See here for the
documentation for the latest stable version:
http://koti.kapsi.fi/~jpa/nanopb/docs/index.html
.. _`Overview`: index.html
.. _`Concepts`: concepts.html
.. _`API reference`: reference.html

View File

@@ -6,6 +6,32 @@ Nanopb: API reference
.. contents ::
Compilation options
===================
The following options can be specified using -D switch given to the C compiler:
============================ ================================================================================================
__BIG_ENDIAN__ Set this if your platform stores integers and floats in big-endian format.
Mixed-endian systems (different layout for ints and floats) are currently not supported.
NANOPB_INTERNALS Set this to expose the field encoder functions that are hidden since nanopb-0.1.3.
PB_MAX_REQUIRED_FIELDS Maximum number of required fields to check for presence. Default value is 64. Increases stack
usage 1 byte per every 8 fields. Compiler warning will tell if you need this.
PB_FIELD_16BIT Add support for tag numbers > 255 and fields larger than 255 bytes or 255 array entries.
Increases code size 3 bytes per each field. Compiler error will tell if you need this.
PB_FIELD_32BIT Add support for tag numbers > 65535 and fields larger than 65535 bytes or 65535 array entries.
Increases code size 9 bytes per each field. Compiler error will tell if you need this.
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.
PB_OLD_CALLBACK_STYLE Use the old function signature (void\* instead of void\*\*) for callback fields. This was the
default until nanopb-0.2.1.
============================ ================================================================================================
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow raising some datatype limits to suit larger messages.
Their need is recognized automatically by C-preprocessor #if-directives in the generated .pb.h files. The default setting is to use
the smallest datatypes (least resources used).
pb.h
====
@@ -22,31 +48,39 @@ LTYPE identifier Value Storage format
==================== ===== ================================================
PB_LTYPE_VARINT 0x00 Integer.
PB_LTYPE_SVARINT 0x01 Integer, zigzag encoded.
PB_LTYPE_FIXED 0x02 Integer or floating point.
PB_LTYPE_BYTES 0x03 Structure with *size_t* field and byte array.
PB_LTYPE_STRING 0x04 Null-terminated string.
PB_LTYPE_SUBMESSAGE 0x05 Submessage structure.
PB_LTYPE_FIXED32 0x02 32-bit integer or floating point.
PB_LTYPE_FIXED64 0x03 64-bit integer or floating point.
PB_LTYPE_BYTES 0x04 Structure with *size_t* field and byte array.
PB_LTYPE_STRING 0x05 Null-terminated string.
PB_LTYPE_SUBMESSAGE 0x06 Submessage structure.
==================== ===== ================================================
The high-order nibble defines whether the field is required, optional, repeated or callback, and whether it is a pointer:
The bits 4-5 define whether the field is required, optional or repeated:
==================== ===== ================================================
HTYPE identifier Value Field handling
==================== ===== ================================================
PB_HTYPE_REQUIRED 0x00 Verify that field exists in decoded message.
PB_HTYPE_OPTIONAL 0x10 Use the structure's *has_fields* bit array to
specify whether the field is present.
PB_HTYPE_ARRAY 0x20 A repeated field with preallocated array.
PB_HTYPE_OPTIONAL 0x10 Use separate *has_<field>* boolean to specify
whether the field is present.
(Unless it is a callback)
PB_HTYPE_REPEATED 0x20 A repeated field with preallocated array.
Separate *<field>_count* for number of items.
PB_HTYPE_CALLBACK 0x30 A field with dynamic storage size, data is
actually a pointer to a structure containing a
callback function.
PB_HTYPE_POINTER 0x80 For required, optional and array non-scalar
fields, uses a pointer type (char* for strings,
pb_bytes_t for bytes, or a pointer for
submessages).
(Unless it is a callback)
==================== ===== ================================================
The bits 6-7 define the how the storage for the field is allocated:
==================== ===== ================================================
ATYPE identifier Value Allocation method
==================== ===== ================================================
PB_ATYPE_STATIC 0x00 Statically allocated storage in the structure.
PB_ATYPE_CALLBACK 0x40 A field with dynamic storage size. Struct field
actually contains a pointer to a callback
function.
==================== ===== ================================================
pb_field_t
----------
Describes a single structure field with memory position in relation to others. The descriptions are usually autogenerated. ::
@@ -63,14 +97,14 @@ Describes a single structure field with memory position in relation to others. T
} pb_packed;
:tag: Tag number of the field or 0 to terminate a list of fields.
:type: LTYPE and HTYPE of the field.
:type: LTYPE, HTYPE and ATYPE of the field.
:data_offset: Offset of field data, relative to the end of the previous field.
:size_offset: Offset of *size_t* count for arrays, relative to field data.
:size_offset: Offset of *bool* flag for optional fields or *size_t* count for arrays, relative to field data.
:data_size: Size of a single data entry, in bytes. For PB_LTYPE_BYTES, the size of the byte array inside the containing structure. For PB_HTYPE_CALLBACK, size of the C data type if known.
:array_size: Maximum number of entries in an array, if it is an array type.
:ptr: Pointer to default value for optional fields, or to submessage description for PB_LTYPE_SUBMESSAGE.
The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will warn "Initializer too large for type" if the limits are exceeded. The types can be changed to larger ones if necessary.
The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will give error if the values are too large. The types can be changed to larger ones by defining *PB_FIELD_16BIT*.
pb_bytes_array_t
----------------
@@ -83,16 +117,6 @@ An byte array with a field for storing the length::
In an actual array, the length of *bytes* may be different.
pb_bytes_t
----------
A byte array with fields for storing the allocated and current lengths::
typedef struct {
size_t alloced;
size_t size;
uint8_t *bytes;
} pb_bytes_array_t;
pb_callback_t
-------------
Part of a message structure, for fields with type PB_HTYPE_CALLBACK::
@@ -100,21 +124,22 @@ Part of a message structure, for fields with type PB_HTYPE_CALLBACK::
typedef struct _pb_callback_t pb_callback_t;
struct _pb_callback_t {
union {
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg);
bool (*encode_buffer)(pb_strstream_t *stream, const pb_field_t *field, const void *arg);
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
} funcs;
void *arg;
};
The *arg* is passed to the callback when calling. It can be used to store any information that the callback might need.
A pointer to the *arg* is passed to the callback when calling. It can be used to store any information that the callback might need.
When calling `pb_encode`_, *funcs.encode* is used, and similarly when calling `pb_encode_buffer`_, *funcs.encode_buffer* is used, and when calling `pb_decode`_, *funcs.decode* is used. The function pointers are stored in the same memory location but are of incompatible types. You can set the function pointer to NULL to skip the field.
Previously the function received just the value of *arg* instead of a pointer to it. This old behaviour can be enabled by defining *PB_OLD_CALLBACK_STYLE*.
When calling `pb_encode`_, *funcs.encode* is used, and similarly when calling `pb_decode`_, *funcs.decode* is used. The function pointers are stored in the same memory location but are of incompatible types. You can set the function pointer to NULL to skip the field.
pb_wire_type_t
--------------
Protocol Buffers wire types. These are used with `pb_encode_tag`_ and `pb_encbuf_tag`_. ::
Protocol Buffers wire types. These are used with `pb_encode_tag`_. ::
typedef enum {
PB_WT_VARINT = 0,
@@ -155,26 +180,22 @@ pb_encode
---------
Encodes the contents of a structure as a protocol buffers message and writes it to output stream. ::
bool pb_encode(pb_ostream_t *stream, const pb_message_t *msg, const void *src_struct);
bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
:stream: Output stream to write to.
:msg: A message descriptor, usually autogenerated.
:fields: A field description array, usually autogenerated.
:src_struct: Pointer to the data that will be serialized.
:returns: True on success, false on IO error, on detectable errors in field description, or if a field encoder returns false.
Normally pb_encode simply walks through the fields description array inside the message descriptor 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
----------------
Encodes an unsigned integer in the varint_ format. ::
.. sidebar:: Encoding fields manually
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.
:value: Value to encode.
:returns: True on success, false on IO error.
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.
.. _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
-------------
@@ -184,7 +205,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.
: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.
pb_encode_tag_for_field
@@ -210,208 +231,71 @@ STRING, BYTES, SUBMESSAGE PB_WT_STRING
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
----------------
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);
:stream: Output stream to write to.
: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.
.. sidebar:: Field encoders
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
pb_encode_fixed32
-----------------
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.
:field: Field description structure. The *ptr* field must be a pointer to a valid *pb_message_t* descriptor for the submessage.
:value: Pointer to a 4-bytes large C variable, for example `uint32_t foo;`.
:returns: True on success, false on IO error.
pb_encode_fixed64
-----------------
Writes 8 bytes to stream and swaps bytes on big-endian architecture. Works for fields of type `fixed64`, `sfixed64` and `double`::
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
:stream: Output stream to write to.
:value: Pointer to a 8-bytes large C variable, for example `uint64_t foo;`.
:returns: True on success, false on IO error.
pb_encode_submessage
--------------------
Encodes a submessage field, including the size header for it. Works for fields of any message type::
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
:stream: Output stream to write to.
:fields: Pointer to the autogenerated field description array for the submessage type, e.g. `MyMessage_fields`.
:src: Pointer to the structure where submessage data is.
: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.
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.
pb_encode_buffer.h
==================
An important note about this module is that data is written from the
back of the buffer to the front. That is, when you call
*pb_buf_write()*, it will place the bytes (in the order you provide
them) before the data currently in the buffer.
pb_strstream_from_buffer
------------------------
Constructs a buffer descriptor. This is just a helper function, it doesn't do anything you couldn't do yourself in a callback function. ::
pb_strstream_t pb_strstream_from_buffer(uint8_t *buf, size_t bufsize);
:buf: Memory buffer to write into.
:bufsize: Maximum number of bytes to write.
:returns: The buffer descriptor.
The descriptor only tracks the amount of space left; it does not count how many bytes have been written.
pb_buf_write
------------
Prepends data to an in-memory buffer. Always use this function, instead of trying to manage the pointers inside the buffer descriptor. ::
bool pb_buf_write(pb_strstream_t *stream, const uint8_t *buf, size_t count);
:stream: Descriptor for buffer to write to.
:buf: Pointer to buffer with the data to be written.
:count: Number of bytes to write.
:returns: True on success, false if maximum length is exceeded.
If there is not enough space, *stream* is not modified.
pb_encode_buffer
----------------
Encodes the contents of a structure as a protocol buffers message and writes it to a buffer. ::
bool pb_encode_buffer(pb_strstream_t *stream, const pb_message_t *msg, const void *src_struct);
:stream: Descriptor for buffer to write to.
:msg: A message descriptor, usually autogenerated.
:src_struct: Pointer to the data that will be serialized.
:returns: True on success, false if the buffer is too small or if a field encoder returns false.
pb_encbuf_varint
----------------
Encodes an unsigned integer in the varint_ format. ::
bool pb_encbuf_varint(pb_strstream_t *stream, uint64_t value);
:stream: Descriptor for buffer to write to. 1-10 bytes will be written.
:value: Value to encode.
:returns: True on success, false on IO error.
.. _varint: http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints
pb_encbuf_tag
-------------
Finishes a field in the Protocol Buffers binary format: encodes the field number and the wire type of the data. ::
bool pb_encbuf_tag(pb_strstream_t *stream, pb_wire_type_t wiretype, int field_number);
:stream: Descriptor for buffer to write to. 1-5 bytes will be written.
: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.
:returns: True on success, false on IO error.
pb_encbuf_tag_for_field
-----------------------
Same as `pb_encbuf_tag`_, except takes the parameters from a *pb_field_t* structure. ::
bool pb_encbuf_tag_for_field(pb_strstream_t *stream, const pb_field_t *field);
:stream: Descriptor for buffer to write to. 1-5 bytes will be written.
:field: Field description structure. Usually autogenerated.
:returns: True on success, false on IO error or unknown field type.
This function only considers the LTYPE of the field. You can use it from your field callbacks, because the source generator writes correct LTYPE also for callback type fields.
Wire type mapping is as follows:
========================= ============
LTYPEs Wire type
========================= ============
VARINT, SVARINT PB_WT_VARINT
FIXED64 PB_WT_64BIT
STRING, BYTES, SUBMESSAGE PB_WT_STRING
FIXED32 PB_WT_32BIT
========================= ============
pb_encbuf_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. ::
bool pb_encbuf_string(pb_strstream_t *stream, const uint8_t *buffer, size_t size);
:stream: Descriptor for buffer to write to.
:buffer: Pointer to string data.
:size: Number of bytes in the string.
:returns: True on success, false on IO error.
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
===========
@@ -439,15 +323,32 @@ Read data from input stream. Always use this function, don't try to call the str
End of file is signalled by *stream->bytes_left* being zero after pb_read returns false.
pb_decode_varint
pb_decode
---------
Read and decode all fields of a structure. Reads until EOF on input stream. ::
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
:stream: Input stream to read from.
:fields: A field description array. Usually autogenerated.
:dest_struct: Pointer to structure where data will be stored.
:returns: True on success, false on IO error, on detectable errors in field description, if a field encoder returns false or if a required field is missing.
In Protocol Buffers binary format, EOF is only allowed between fields. If it happens anywhere else, pb_decode will return *false*. If pb_decode returns false, you cannot trust any of the data in the structure.
In addition to EOF, the pb_decode implementation supports terminating a message with a 0 byte. This is compatible with the official Protocol Buffers because 0 is never a valid field tag.
For optional fields, this function applies the default value and sets *has_<field>* to false if the field is not present.
pb_decode_noinit
----------------
Read and decode a varint_ encoded integer. ::
Same as `pb_decode`_, except does not apply the default values to fields. ::
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
:stream: Input stream to read from. 1-10 bytes will be read.
:dest: Storage for the decoded integer. Value is undefined on error.
:returns: True on success, false if value exceeds uint64_t range or an IO error happens.
(parameters are the same as for `pb_decode`_.)
The destination structure should be filled with zeros before calling this function. Doing a *memset* manually can be slightly faster than using `pb_decode`_ if you don't need any default values.
pb_skip_varint
--------------
@@ -467,127 +368,106 @@ Skip a varint-length-prefixed string. This means skipping a value with wire type
:stream: Input stream to read from.
:returns: True on success, false on IO error or length exceeding uint32_t.
pb_decode
---------
Read and decode all fields of a structure. Reads until EOF on input stream. ::
pb_decode_tag
-------------
Decode the tag that comes before field in the protobuf encoding::
bool pb_decode(pb_istream_t *stream, const pb_message_t *msg, void *dest_struct);
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, int *tag, bool *eof);
:stream: Input stream to read from.
:msg: A message descriptor. Usually autogenerated.
:dest_struct: Pointer to structure where data will be stored.
:returns: True on success, false on IO error, on detectable errors in field description, if a field encoder returns false or if a required field is missing.
:wire_type: Pointer to variable where to store the wire type of the field.
:tag: Pointer to variable where to store the tag of the field.
:eof: Pointer to variable where to store end-of-file status.
:returns: True on success, false on error or EOF.
In Protocol Buffers binary format, EOF is only allowed between fields. If it happens anywhere else, pb_decode will return *false*. If pb_decode returns false, you cannot trust any of the data in the structure.
When the message (stream) ends, this function will return false and set *eof* to true. On other
errors, *eof* will be set to false.
In addition to EOF, the pb_decode implementation supports terminating a message with a 0 byte. This is compatible with the official Protocol Buffers because 0 is never a valid field tag.
For optional fields, this function applies the default value and clears the corresponding bit in *has_fields* if the field is not present.
pb_clean
--------
Release and clear all the unused pointers of a structure. ::
bool pb_clean(const pb_message_t *msg, void *dest_struct);
:msg: A message descriptor. Usually autogenerated.
:dest_struct: Pointer to structure to be cleaned.
:returns: True on success, false if one of the unused message fields has a pointer type that this function cannot handle.
For each string or submessage with pointer type, this function calls *free()* on the pointer and sets the pointer to NULL.
For bytes fields with pointer type, this function calls *free()* on the allocated data, nulls that pointer and zeros the size fields in the generated pb_bytes_t structure.
For repeated fields, it applies the corresponding operation above to each unused element of the generated array.
.. sidebar:: Field decoders
The functions with names beginning with *pb_dec_* are called field decoders. Each PB_LTYPE has an own field decoder, which handles translating from Protocol Buffers data to C data.
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*.
pb_dec_varint
pb_skip_field
-------------
Field decoder for PB_LTYPE_VARINT. ::
Remove the data for a field from the stream, without actually decoding it::
bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest)
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
:stream: Input stream to read from.
:wire_type: Type of field to skip.
:returns: True on success, false on IO error.
.. sidebar:: Decoding fields manually
The functions with names beginning with *pb_decode_* are used when dealing with callback fields. The typical reason for using callbacks is to have an array of unlimited size. In that case, `pb_decode`_ will call your callback function repeatedly, which can then store the values into e.g. filesystem in the order received in.
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->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.
pb_decode_varint
----------------
Read and decode a varint_ encoded integer. ::
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
:stream: Input stream to read from. 1-10 bytes will be read.
:field: Field description structure. Only *field->data_size* matters.
:dest: Pointer to destination integer. Must have size of *field->data_size* bytes.
:returns: True on success, false on IO errors or if `pb_decode_varint`_ fails.
:dest: Storage for the decoded integer. Value is undefined on error.
:returns: True on success, false if value exceeds uint64_t range or an IO error happens.
This function first calls `pb_decode_varint`_. It then copies the first bytes of the 64-bit result value to *dest*, or on big endian architectures, the last bytes.
pb_decode_svarint
-----------------
Similar to `pb_decode_varint`_, except that it performs zigzag-decoding on the value. This corresponds to the Protocol Buffers *sint32* and *sint64* datatypes. ::
pb_dec_svarint
--------------
Field decoder for PB_LTYPE_SVARINT. Similar to `pb_dec_varint`_, except that it performs zigzag-decoding on the value. ::
bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
(parameters are the same as `pb_decode_varint`_)
(parameters are the same as `pb_dec_varint`_)
pb_decode_fixed32
-----------------
Decode a *fixed32*, *sfixed32* or *float* value. ::
pb_dec_fixed32
--------------
Field decoder for PB_LTYPE_FIXED32. ::
bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
bool pb_dec_fixed(pb_istream_t *stream, const pb_field_t *field, void *dest);
:stream: Input stream to read from. 1-10 bytes will be read.
:field: Not used.
:dest: Pointer to destination integer. Must have size of *field->data_size* bytes.
:returns: True on success, false on IO errors or if `pb_decode_varint`_ fails.
:stream: Input stream to read from. 4 bytes will be read.
:dest: Pointer to destination *int32_t*, *uint32_t* or *float*.
:returns: True on success, false on IO errors.
This function reads 4 bytes from the input stream.
On big endian architectures, it then reverses the order of the bytes.
Finally, it writes the bytes to *dest*.
pb_dec_fixed64
--------------
Field decoder for PB_LTYPE_FIXED64. ::
pb_decode_fixed64
-----------------
Decode a *fixed64*, *sfixed64* or *double* value. ::
bool pb_dec_fixed(pb_istream_t *stream, const pb_field_t *field, void *dest);
Same as `pb_dec_fixed32`_, except this reads 8 bytes.
: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.
pb_dec_bytes
------------
Field decoder for PB_LTYPE_BYTES. Reads a length-prefixed block of bytes. ::
Same as `pb_decode_fixed32`_, except this reads 8 bytes.
bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
pb_make_string_substream
------------------------
Decode the length for a field with wire type *PB_WT_STRING* and create a substream for reading the data. ::
:stream: Input stream to read from.
:field: Field description structure. Only *field->data_size* matters.
:dest: Pointer to a structure similar to pb_bytes_array_t.
:returns: True on success, false on IO error or if length exceeds the array size.
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
This function expects a pointer to a structure with a *size_t* field at start, and a variable sized byte array after it. It will deduce the maximum size of the array from *field->data_size*.
:stream: Original input stream to read the length and data from.
:substream: New substream that has limited length. Filled in by the function.
:returns: True on success, false if reading the length fails.
pb_dec_string
-------------
Field decoder for PB_LTYPE_STRING. Reads a length-prefixed string. ::
This function uses `pb_decode_varint`_ to read an integer from the stream. This is interpreted as a number of bytes, and the substream is set up so that its `bytes_left` is initially the same as the length, and its callback function and state the same as the parent stream.
bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
pb_close_string_substream
-------------------------
Close the substream created with `pb_make_string_substream`_. ::
:stream: Input stream to read from.
:field: Field description structure. Only *field->data_size* matters.
:dest: Pointer to a character array of size *field->data_size*.
:returns: True on success, false on IO error or if length exceeds the array size.
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
This function null-terminates the string when successful. On error, the contents of the destination array is undefined.
pb_dec_submessage
-----------------
Field decoder for PB_LTYPE_SUBMESSAGE. Calls `pb_decode`_ to perform the actual decoding. ::
bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
:stream: Input stream to read from.
:field: Field description structure. Only *field->ptr* matters.
:dest: Pointer to the destination structure.
:returns: True on success, false on IO error or if `pb_decode`_ fails.
The *field->ptr* should be a pointer to *pb_message_t* describing the submessage.
:stream: Original input stream to read the length and data from.
:substream: Substream to close
This function copies back the state from the substream to the parent stream.
It must be called after done with the substream.

View File

@@ -23,14 +23,14 @@
#include "fileproto.pb.h"
#include "common.h"
bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
FileInfo fileinfo;
if (!pb_decode(stream, FileInfo_msg, &fileinfo))
if (!pb_decode(stream, FileInfo_fields, &fileinfo))
return false;
printf("%-10lld %s\n", fileinfo.inode, fileinfo.name);
printf("%-10lld %s\n", (long long)fileinfo.inode, fileinfo.name);
return true;
}
@@ -45,11 +45,11 @@ bool listdir(int fd, char *path)
if (path == NULL)
{
ListFilesRequest_clear(request, path);
request.has_path = false;
}
else
{
ListFilesRequest_set(request, path);
request.has_path = true;
if (strlen(path) + 1 > sizeof(request.path))
{
fprintf(stderr, "Too long path.\n");
@@ -59,7 +59,7 @@ bool listdir(int fd, char *path)
strcpy(request.path, path);
}
if (!pb_encode(&output, ListFilesRequest_msg, &request))
if (!pb_encode(&output, ListFilesRequest_fields, &request))
{
fprintf(stderr, "Encoding failed.\n");
return false;
@@ -70,9 +70,9 @@ bool listdir(int fd, char *path)
response.file.funcs.decode = &printfile_callback;
if (!pb_decode(&input, ListFilesResponse_msg, &response))
if (!pb_decode(&input, ListFilesResponse_fields, &response))
{
fprintf(stderr, "Decoding failed.\n");
fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&input));
return false;
}

View File

@@ -10,24 +10,15 @@
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;
}
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;
if (buf == NULL)
{
/* Well, this is a really inefficient way to skip input. */
/* It is only used when there are unknown fields. */
char dummy;
while (count-- && recv(fd, &dummy, 1, 0) == 1);
return count == 0;
}
result = recv(fd, buf, count, MSG_WAITALL);
if (result == 0)
@@ -38,12 +29,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 stream = {&write_callback, (void*)fd, SIZE_MAX, 0};
pb_ostream_t stream = {&write_callback, (void*)(intptr_t)fd, SIZE_MAX, 0};
return stream;
}
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;
}

View File

@@ -23,9 +23,9 @@
#include "fileproto.pb.h"
#include "common.h"
bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
DIR *dir = (DIR*) arg;
DIR *dir = (DIR*) *arg;
struct dirent *file;
FileInfo fileinfo;
@@ -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))
return false;
if (!pb_enc_submessage(stream, field, &fileinfo))
if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
return false;
}
@@ -53,9 +53,9 @@ void handle_connection(int connfd)
pb_ostream_t output = pb_ostream_from_socket(connfd);
DIR *directory;
if (!pb_decode(&input, ListFilesRequest_msg, &request))
if (!pb_decode(&input, ListFilesRequest_fields, &request))
{
printf("Decoding failed.\n");
printf("Decode failed: %s\n", PB_GET_ERROR(&input));
return;
}
@@ -67,18 +67,18 @@ void handle_connection(int connfd)
{
perror("opendir");
ListFilesResponse_set(response, path_error);
response.has_path_error = true;
response.path_error = true;
response.file.funcs.encode = NULL;
}
else
{
ListFilesResponse_clear(response, path_error);
response.has_path_error = false;
response.file.funcs.encode = &listdir_callback;
response.file.arg = directory;
}
if (!pb_encode(&output, ListFilesResponse_msg, &response))
if (!pb_encode(&output, ListFilesResponse_fields, &response))
{
printf("Encoding failed.\n");
}

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

17
example_unions/Makefile Normal file
View File

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

96
example_unions/decode.c Normal file
View File

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

85
example_unions/encode.c Normal file
View File

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

View File

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

View File

@@ -1,19 +1,44 @@
// Custom options for defining:
// - Maximum size of string/bytes
// - Maximum number of elements in array
// - Pointer or in-line representation of non-scalar fields
//
// These are used by nanopb to generate statically allocable structures
// for memory-limited environments.
import "google/protobuf/descriptor.proto";
message NanoPBOptions {
optional int32 max_size = 1;
optional int32 max_count = 2;
optional bool pointer = 3;
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.
}
// This is the inner options message, which basically defines options for
// a field. When it is used in message or file scope, it applies to all
// fields.
message NanoPBOptions {
// Allocated size for 'bytes' and 'string' fields.
optional int32 max_size = 1;
// Allocated number of entries in arrays ('repeated' fields)
optional int32 max_count = 2;
// Force type of field (callback or static allocation)
optional FieldType type = 3 [default = FT_DEFAULT];
// 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];
}
// Extensions to protoc 'Descriptor' type in order to define options
// inside a .proto file.
//
// Protocol Buffers extension number registry
// --------------------------------
// Project: Nanopb
@@ -22,7 +47,20 @@ message NanoPBOptions {
// Extensions: 1010 (all types)
// --------------------------------
extend google.protobuf.FileOptions {
optional NanoPBOptions nanopb_fileopt = 1010;
}
extend google.protobuf.MessageOptions {
optional NanoPBOptions nanopb_msgopt = 1010;
}
extend google.protobuf.EnumOptions {
optional NanoPBOptions nanopb_enumopt = 1010;
}
extend google.protobuf.FieldOptions {
optional NanoPBOptions nanopb = 1010;
}

View File

@@ -1,38 +1,61 @@
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
nanopb_version = "nanopb-0.2.1-dev"
import google.protobuf.descriptor_pb2 as descriptor
import nanopb_pb2
try:
import google.protobuf.descriptor_pb2 as descriptor
import google.protobuf.text_format as text_format
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
# ---------------------------------------------------------------------------
# Generation of single fields
# ---------------------------------------------------------------------------
import time
import os.path
from optparse import OptionParser
# 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_FIXED'),
FieldD.TYPE_SFIXED64: ('int64_t', 'PB_LTYPE_FIXED'),
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')
}
pointable_types = frozenset([
FieldD.TYPE_STRING,
FieldD.TYPE_BYTES,
FieldD.TYPE_MESSAGE
])
options = None
class Names:
'''Keeps a set of nested names and formats them to C identifier.
You can subclass this with your own implementation.
'''
'''Keeps a set of nested names and formats them to C identifier.'''
def __init__(self, parts = ()):
if isinstance(parts, Names):
parts = parts.parts
@@ -49,6 +72,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] != '.':
@@ -56,19 +82,27 @@ def names_from_type_name(type_name):
return Names(type_name[1:].split('.'))
class Enum:
def __init__(self, names, desc):
def __init__(self, names, desc, enum_options):
'''desc is EnumDescriptorProto'''
self.options = enum_options
self.names = names + desc.name
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 {\n'
result = 'typedef enum _%s {\n' % self.names
result += ',\n'.join([" %s = %d" % x for x in self.values])
result += '\n} %s;' % self.names
return result
class Field:
def __init__(self, struct_name, desc):
def __init__(self, struct_name, desc, field_options):
'''desc is FieldDescriptorProto'''
self.tag = desc.number
self.struct_name = struct_name
@@ -77,101 +111,93 @@ class Field:
self.max_size = None
self.max_count = None
self.array_decl = ""
self.is_pointer = options.pointer
self.is_array = False
# Parse nanopb-specific field options
if desc.options.HasExtension(nanopb_pb2.nanopb):
ext = desc.options.Extensions[nanopb_pb2.nanopb]
if ext.HasField("max_size"):
self.max_size = ext.max_size
if ext.HasField("max_count"):
self.max_count = ext.max_count
if ext.HasField("pointer"):
self.is_pointer = ext.pointer
if desc.type not in pointable_types:
raise NotImplementedError('Cannot make %s.%s a pointer'
% (struct_name, desc.name))
# Parse field options
if field_options.HasField("max_size"):
self.max_size = field_options.max_size
if field_options.HasField("max_count"):
self.max_count = field_options.max_count
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.
is_callback = False
# 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:
is_callback = True
can_be_static = False
else:
self.htype = 'PB_HTYPE_ARRAY'
self.array_decl = '[%d]' % self.max_count
self.is_array = True
else:
raise NotImplementedError(desc.label)
if self.is_pointer:
self.htype += ' | PB_HTYPE_POINTER'
# 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'
if self.is_pointer:
self.ctype = 'char'
elif self.max_size is None:
is_callback = True
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'
if self.is_pointer:
self.ctype = 'pb_bytes_t'
elif self.max_size is None:
is_callback = True
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)
if is_callback:
self.htype = 'PB_HTYPE_CALLBACK'
if field_options.type == nanopb_pb2.FT_DEFAULT:
if can_be_static:
field_options.type = nanopb_pb2.FT_STATIC
else:
field_options.type = nanopb_pb2.FT_CALLBACK
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_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.is_array:
if self.rules == 'OPTIONAL' and self.allocation == 'STATIC':
result = ' bool has_' + self.name + ';\n'
elif self.rules == 'REPEATED' and self.allocation == 'STATIC':
result = ' size_t ' + self.name + '_count;\n'
else:
result = ''
if self.is_pointer and self.ctype != 'pb_bytes_t':
result += ' %s *%s%s;' % (self.ctype, self.name, self.array_decl)
else:
result += ' %s %s%s;' % (self.ctype, self.name, self.array_decl)
return result
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
@@ -185,29 +211,24 @@ class Field:
if self.default is None:
return None
if self.ltype == 'PB_LTYPE_STRING':
ctype = 'char'
if self.max_size is None:
ctype, default = self.ctype, self.default
array_decl = ''
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)
@@ -218,59 +239,84 @@ 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 += self.htype
if self.ltype is not None:
result += ' | ' + 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.is_array:
result += '\n pb_delta(%s, %s_count, %s),' % (self.struct_name, self.name, self.name)
else:
result += ' 0,'
if self.is_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))
elif self.htype != 'PB_HTYPE_CALLBACK' and not self.is_pointer and self.ltype == 'PB_LTYPE_BYTES':
result += '\n pb_membersize(%s, bytes),' % self.ctype
result += ' 0,'
else:
result += '\n pb_membersize(%s, %s),' % (self.struct_name, self.name)
result += ' 0,'
if self.ltype == 'PB_LTYPE_SUBMESSAGE':
result += '\n %s_msg}' % 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.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)
return max(self.tag, self.max_size, self.max_count)
# ---------------------------------------------------------------------------
# Generation of messages (structures)
# ---------------------------------------------------------------------------
class Message:
def __init__(self, names, desc):
def __init__(self, names, desc, message_options):
self.name = names
self.fields = [Field(self.name, f) for f in desc.field]
self.fields = []
for f in desc.field:
field_options = get_nanopb_suboptions(f, message_options, self.name + f.name)
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()
def get_dependencies(self):
'''Get list of type names that this structure refers to.'''
return [str(field.ctype) for field in self.fields if not
(field.is_pointer or field.htype == 'PB_HTYPE_CALLBACK')]
return [str(field.ctype) for field in self.fields]
def __str__(self):
result = 'struct %s {\n' % self.name
result += ' uint8_t has_fields[%d];\n' % ((len(self.fields) + 7) / 8)
result = 'typedef struct _%s {\n' % self.name
if not self.ordered_fields:
# Empty structs are not allowed in C standard.
# Therefore add a dummy field if an empty message occurs.
result += ' uint8_t dummy_field;'
result += '\n'.join([str(f) for f in self.ordered_fields])
result += '\n};'
result += '\n}'
if self.packed:
result += ' pb_packed'
result += ' %s;' % self.name
if self.packed:
result = 'PB_PACKED_STRUCT_START\n' + result
result += '\nPB_PACKED_STRUCT_END'
return result
def types(self):
@@ -281,9 +327,6 @@ class Message:
result += types + '\n'
return result
def typedef(self):
return 'typedef struct %s %s;' % (self.name, self.name)
def default_decl(self, declaration_only = False):
result = ""
for field in self.fields:
@@ -292,37 +335,31 @@ class Message:
result += default + '\n'
return result
def message_declaration(self):
result = 'typedef PB_MSG_STRUCT(%d) %s_msg_t;\n' % (len(self.fields), self.name)
result += 'extern const %s_msg_t %s_real_msg;\n' % (self.name, self.name)
result += '#define %s_msg ((const pb_message_t*)&%s_real_msg)\n' % (self.name, self.name)
def fields_declaration(self):
result = 'extern const pb_field_t %s_fields[%d];' % (self.name, len(self.fields) + 1)
return result
def message_definition(self):
result = 'const %s_msg_t %s_real_msg = {\n' % (self.name, self.name)
result += ' %d, sizeof(%s),\n' % (len(self.fields), self.name)
def fields_definition(self):
result = 'const pb_field_t %s_fields[%d] = {\n' % (self.name, len(self.fields) + 1)
result += ' {\n\n'
prev = None
for field in self.ordered_fields:
result += field.pb_field_t(prev)
result += ',\n\n'
result += ',\n'
prev = field.name
result += ' }\n};'
result += ' PB_LAST_FIELD\n};'
return result
def field_numbers(self):
result = '#define %s_has(STRUCT, FIELD) PB_HAS_FIELD(STRUCT, %s, FIELD)\n' % (self.name, self.name)
result += '#define %s_set(STRUCT, FIELD) PB_SET_FIELD(STRUCT, %s, FIELD)\n' % (self.name, self.name)
result += '#define %s_clear(STRUCT, FIELD) PB_CLEAR_FIELD(STRUCT, %s, FIELD)\n' % (self.name, self.name)
i = 0
for field in self.ordered_fields:
result += '#define %s_%s_index %d\n' % (self.name, field.name, i)
result += '#define %s_%s_tag %d\n' % (self.name, field.name, field.tag)
i += 1
return result
# ---------------------------------------------------------------------------
# Processing of entire .proto files
# ---------------------------------------------------------------------------
def iterate_messages(desc, names = Names()):
'''Recursively find all messages. For each, yield name, DescriptorProto.'''
@@ -338,7 +375,7 @@ def iterate_messages(desc, names = Names()):
for x in iterate_messages(submsg, sub_names):
yield x
def parse_file(fdesc):
def parse_file(fdesc, file_options):
'''Takes a FileDescriptorProto and returns tuple (enum, messages).'''
enums = []
@@ -350,12 +387,24 @@ def parse_file(fdesc):
base_name = Names()
for enum in fdesc.enum_type:
enums.append(Enum(base_name, enum))
enum_options = get_nanopb_suboptions(enum, file_options, base_name + enum.name)
enums.append(Enum(base_name, enum, enum_options))
for names, message in iterate_messages(fdesc, base_name):
messages.append(Message(names, message))
message_options = get_nanopb_suboptions(message, file_options, names)
messages.append(Message(names, message, message_options))
for enum in message.enum_type:
enums.append(Enum(names, enum))
enum_options = get_nanopb_suboptions(enum, message_options, names + enum.name)
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
@@ -366,7 +415,7 @@ def toposort2(data):
'''
for k, v in data.items():
v.discard(k) # Ignore self dependencies
extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys())
extra_items_in_deps = reduce(set.union, data.values(), set()) - set(data.keys())
data.update(dict([(item, set()) for item in extra_items_in_deps]))
while True:
ordered = set(item for item,dep in data.items() if not dep)
@@ -390,27 +439,47 @@ def sort_dependencies(messages):
if msgname in message_by_name:
yield message_by_name[msgname]
def generate_header(headername, enums, messages):
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, options):
'''Generate content for a header file.
Generates strings, which should be concatenated and stored to file.
'''
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'
try:
yield options.libformat % ('pb.h')
except TypeError:
# no %s specified - use whatever was passed in as options.libformat
yield options.libformat
yield '\n'
for dependency in dependencies:
noext = os.path.splitext(dependency)[0]
yield options.genformat % (noext + '.' + options.extension + '.h')
yield '\n'
yield '#ifdef __cplusplus\n'
yield 'extern "C" {\n'
yield '#endif\n\n'
yield '/* Enum definitions */\n'
for enum in enums:
yield str(enum) + '\n\n'
yield '/* Struct typedefs */\n'
for msg in messages:
yield msg.typedef() + '\n'
yield '\n'
yield '/* Struct definitions */\n'
for msg in sort_dependencies(messages):
yield msg.types()
@@ -423,20 +492,22 @@ def generate_header(headername, enums, messages):
yield '/* Struct field encoding specification for nanopb */\n'
for msg in messages:
yield msg.message_declaration() + '\n'
yield '\n'
yield msg.fields_declaration() + '\n'
yield '/* Field indexes and tags */\n'
for msg in messages:
yield msg.field_numbers()
yield '\n#ifdef __cplusplus\n'
yield '} /* extern "C" */\n'
yield '#endif\n'
# End of header
yield '\n#endif\n'
def generate_source(headername, enums, messages):
'''Generate content for a source file.'''
yield '/* Automatically generated nanopb constant definitions */\n'
yield '#include "%s"\n\n' % headername
yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
yield options.genformat % (headername)
yield '\n'
for msg in messages:
yield msg.default_decl(False)
@@ -444,43 +515,233 @@ def generate_source(headername, enums, messages):
yield '\n\n'
for msg in messages:
yield msg.message_definition() + '\n\n'
yield msg.fields_definition() + '\n\n'
if __name__ == '__main__':
import sys
import os.path
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'
parser = OptionParser(usage="Usage: %prog [options] file.pb",
epilog=
"""file.pb should be compiled from file.proto by:
protoc -ofile.pb file.proto
""")
parser.add_option("-p", "--pointer", dest="pointer",
action="store_true", default=False,
help="generate pointers for non-scalar fields")
# 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)
(options, args) = parser.parse_args()
if worst > 255 or checks:
yield '\n/* Check that field information fits in pb_field_t */\n'
if len(args) != 1:
parser.error("Please provide exactly one file.pb argument")
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'
data = open(args[0]).read()
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'
# ---------------------------------------------------------------------------
# Options parsing for the .proto files
# ---------------------------------------------------------------------------
from fnmatch import fnmatch
def read_options_file(infile):
'''Parse a separate options file to list:
[(namemask, options), ...]
'''
results = []
for line in infile:
line = line.strip()
if not line or line.startswith('//') or line.startswith('#'):
continue
parts = line.split(None, 1)
opts = nanopb_pb2.NanoPBOptions()
text_format.Merge(parts[1], opts)
results.append((parts[0], opts))
return results
class Globals:
'''Ugly global variables, should find a good way to pass these.'''
verbose_options = False
separate_options = []
def get_nanopb_suboptions(subdesc, options, name):
'''Get copy of options, and merge information from subdesc.'''
new_options = nanopb_pb2.NanoPBOptions()
new_options.CopyFrom(options)
# Handle options defined in a separate file
dotname = '.'.join(name.parts)
for namemask, options in Globals.separate_options:
if fnmatch(dotname, namemask):
new_options.MergeFrom(options)
# Handle options defined in .proto
if isinstance(subdesc.options, descriptor.FieldOptions):
ext_type = nanopb_pb2.nanopb
elif isinstance(subdesc.options, descriptor.FileOptions):
ext_type = nanopb_pb2.nanopb_fileopt
elif isinstance(subdesc.options, descriptor.MessageOptions):
ext_type = nanopb_pb2.nanopb_msgopt
elif isinstance(subdesc.options, descriptor.EnumOptions):
ext_type = nanopb_pb2.nanopb_enumopt
else:
raise Exception("Unknown options type")
if subdesc.options.HasExtension(ext_type):
ext = subdesc.options.Extensions[ext_type]
new_options.MergeFrom(ext)
if Globals.verbose_options:
print "Options for " + dotname + ":"
print text_format.MessageToString(new_options)
return new_options
# ---------------------------------------------------------------------------
# Command line interface
# ---------------------------------------------------------------------------
import sys
import os.path
from optparse import OptionParser
optparser = OptionParser(
usage = "Usage: nanopb_generator.py [options] file.pb ...",
epilog = "Compile file.pb from file.proto by: 'protoc -ofile.pb file.proto'. " +
"Output will be written to file.pb.h and file.pb.c.")
optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[],
help="Exclude file from generated #include list.")
optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default="pb",
help="Set extension to use instead of 'pb' for generated files. [default: %default]")
optparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE", default="%s.options",
help="Set name of a separate generator options file.")
optparser.add_option("-Q", "--generated-include-format", dest="genformat",
metavar="FORMAT", default='#include "%s"\n',
help="Set format string to use for including other .pb.h files. [default: %default]")
optparser.add_option("-L", "--library-include-format", dest="libformat",
metavar="FORMAT", default='#include <%s>\n',
help="Set format string to use for including the nanopb pb.h header. [default: %default]")
optparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
help="Don't print anything except errors.")
optparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
help="Print more information.")
optparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[],
help="Set generator option (max_size, max_count etc.).")
def process(filenames, options):
'''Process the files given on the command line.'''
if not filenames:
optparser.print_help()
return False
if options.quiet:
options.verbose = False
Globals.verbose_options = options.verbose
toplevel_options = nanopb_pb2.NanoPBOptions()
for s in options.settings:
text_format.Merge(s, toplevel_options)
for filename in filenames:
data = open(filename, 'rb').read()
fdesc = descriptor.FileDescriptorSet.FromString(data)
enums, messages = parse_file(fdesc.file[0])
noext = os.path.splitext(args[0])[0]
headername = noext + '.pb.h'
sourcename = noext + '.pb.c'
# Check if any separate options are specified
try:
optfilename = options.options_file % os.path.splitext(filename)[0]
except TypeError:
# No %s specified, use the filename as-is
optfilename = options.options_file
if options.verbose:
print 'Reading options from ' + optfilename
if os.path.isfile(optfilename):
Globals.separate_options = read_options_file(open(optfilename, "rU"))
else:
Globals.separate_options = []
# Parse the file
file_options = get_nanopb_suboptions(fdesc.file[0], toplevel_options, Names([filename]))
enums, messages = parse_file(fdesc.file[0], file_options)
noext = os.path.splitext(filename)[0]
headername = noext + '.' + options.extension + '.h'
sourcename = noext + '.' + options.extension + '.c'
headerbasename = os.path.basename(headername)
if not options.quiet:
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'] + options.exclude
dependencies = [d for d in fdesc.file[0].dependency if d not in excludes]
header = open(headername, 'w')
for part in generate_header(headerbasename, enums, messages):
for part in generate_header(dependencies, headerbasename, enums, messages, options):
header.write(part)
source = open(sourcename, 'w')
for part in generate_source(headerbasename, enums, messages):
source.write(part)
return True
if __name__ == '__main__':
options, filenames = optparser.parse_args()
status = process(filenames, options)
if not status:
sys.exit(1)

View File

@@ -12,12 +12,70 @@ import google.protobuf.descriptor_pb2
DESCRIPTOR = descriptor.FileDescriptor(
name='nanopb.proto',
package='',
serialized_pb='\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"E\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12\x0f\n\x07pointer\x18\x03 \x01(\x08:>\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',
full_name='FieldType',
filename=None,
file=DESCRIPTOR,
values=[
descriptor.EnumValueDescriptor(
name='FT_DEFAULT', index=0, number=0,
options=None,
type=None),
descriptor.EnumValueDescriptor(
name='FT_CALLBACK', index=1, number=1,
options=None,
type=None),
descriptor.EnumValueDescriptor(
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=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(
name='nanopb_fileopt', full_name='nanopb_fileopt', index=0,
number=1010, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
options=None)
NANOPB_MSGOPT_FIELD_NUMBER = 1010
nanopb_msgopt = descriptor.FieldDescriptor(
name='nanopb_msgopt', full_name='nanopb_msgopt', index=1,
number=1010, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
options=None)
NANOPB_ENUMOPT_FIELD_NUMBER = 1010
nanopb_enumopt = descriptor.FieldDescriptor(
name='nanopb_enumopt', full_name='nanopb_enumopt', index=2,
number=1010, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=True, extension_scope=None,
options=None)
NANOPB_FIELD_NUMBER = 1010
nanopb = descriptor.FieldDescriptor(
name='nanopb', full_name='nanopb', index=0,
name='nanopb', full_name='nanopb', index=3,
number=1010, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
@@ -47,9 +105,23 @@ _NANOPBOPTIONS = descriptor.Descriptor(
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='pointer', full_name='NanoPBOptions.pointer', index=2,
number=3, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
name='type', full_name='NanoPBOptions.type', index=2,
number=3, type=14, cpp_type=8, label=1,
has_default_value=True, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='long_names', full_name='NanoPBOptions.long_names', index=3,
number=4, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=True,
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),
@@ -62,10 +134,11 @@ _NANOPBOPTIONS = descriptor.Descriptor(
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=50,
serialized_end=119,
serialized_start=51,
serialized_end=197,
)
_NANOPBOPTIONS.fields_by_name['type'].enum_type = _FIELDTYPE
DESCRIPTOR.message_types_by_name['NanoPBOptions'] = _NANOPBOPTIONS
class NanoPBOptions(message.Message):
@@ -74,6 +147,12 @@ class NanoPBOptions(message.Message):
# @@protoc_insertion_point(class_scope:NanoPBOptions)
nanopb_fileopt.message_type = _NANOPBOPTIONS
google.protobuf.descriptor_pb2.FileOptions.RegisterExtension(nanopb_fileopt)
nanopb_msgopt.message_type = _NANOPBOPTIONS
google.protobuf.descriptor_pb2.MessageOptions.RegisterExtension(nanopb_msgopt)
nanopb_enumopt.message_type = _NANOPBOPTIONS
google.protobuf.descriptor_pb2.EnumOptions.RegisterExtension(nanopb_enumopt)
nanopb.message_type = _NANOPBOPTIONS
google.protobuf.descriptor_pb2.FieldOptions.RegisterExtension(nanopb)
# @@protoc_insertion_point(module_scope)

276
pb.h
View File

@@ -6,15 +6,64 @@
* see pb_encode.h or pb_decode.h
*/
#define NANOPB_VERSION nanopb-0.2.1-dev
#ifdef PB_SYSTEM_HEADER
#include PB_SYSTEM_HEADER
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#endif
#ifdef __GNUC__
/* This just reduces memory requirements, but is not required. */
#define pb_packed __attribute__((packed))
/* Macro for defining packed structures (compiler dependent).
* This just reduces memory requirements, but is not required.
*/
#if defined(__GNUC__) || defined(__clang__)
/* For GCC and clang */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed __attribute__((packed))
#elif defined(__ICCARM__)
/* For IAR ARM compiler */
# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
# define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
# define pb_packed
#elif defined(_MSC_VER)
/* For Microsoft Visual C++ */
# define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
# define PB_PACKED_STRUCT_END __pragma(pack(pop))
# define pb_packed
#else
#define pb_packed
/* Unknown compiler */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed
#endif
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif
/* Compile-time assertion, used for checking compatible compilation options.
* 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_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
* (change here or on compiler command line). */
#ifndef PB_MAX_REQUIRED_FIELDS
#define PB_MAX_REQUIRED_FIELDS 64
#endif
#if PB_MAX_REQUIRED_FIELDS < 64
#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
#endif
/* List of possible field types. These are used in the autogenerated code.
@@ -27,12 +76,12 @@
* SINT* is different, though, because it is zig-zag coded.
*/
typedef uint8_t pb_type_t;
/************************
* Field contents types *
************************/
typedef uint8_t pb_type_t;
/* Numeric types */
#define PB_LTYPE_VARINT 0x00 /* int32, uint32, int64, uint64, bool, enum */
#define PB_LTYPE_SVARINT 0x01 /* sint32, sint64 */
@@ -56,106 +105,81 @@ typedef uint8_t pb_type_t;
/* Number of declared LTYPES */
#define PB_LTYPES_COUNT 7
#define PB_LTYPE_MASK 0x0F
/******************
* Modifier flags *
******************/
/**************************
* Field repetition rules *
**************************/
/* Just the basic, write data at data_offset */
#define PB_HTYPE_REQUIRED 0x00
/* Write true at size_offset */
#define PB_HTYPE_OPTIONAL 0x10
#define PB_HTYPE_REPEATED 0x20
#define PB_HTYPE_MASK 0x30
/* Read to pre-allocated array
* Maximum number of entries is array_size,
* actual number is stored at size_offset */
#define PB_HTYPE_ARRAY 0x20
/********************
* Allocation types *
********************/
/* 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). */
#define PB_HTYPE_CALLBACK 0x30
#define PB_ATYPE_STATIC 0x00
#define PB_ATYPE_CALLBACK 0x40
#define PB_ATYPE_MASK 0xC0
/* Indicates that a string, bytes or non-repeated submessage is
* represented using a pointer (char* for string, pb_byptes_array_t
* for bytes).
*/
#define PB_HTYPE_POINTER 0x80
#define PB_POINTER(x) ((x) & PB_HTYPE_POINTER)
#define PB_HTYPE(x) ((x) & 0x70)
#define PB_LTYPE(x) ((x) & 0x0F)
#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
/* This structure is used in auto-generated constants
* to specify struct fields.
* You can change field sizes here if you need structures
* You can change field sizes if you need structures
* larger than 256 bytes or field tags larger than 256.
* The compiler should complain if your .proto has such
* structures ("initializer too large for type").
* structures. Fix that by defining PB_FIELD_16BIT or
* PB_FIELD_32BIT.
*/
PB_PACKED_STRUCT_START
typedef struct _pb_field_t pb_field_t;
struct _pb_field_t {
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
uint8_t tag;
pb_type_t type;
uint8_t data_offset; /* Offset of field data, relative to previous field. */
int8_t size_offset; /* Offset of array size or has-boolean, relative to data */
uint8_t data_size; /* Data size in bytes for a single item */
uint8_t array_size; /* Maximum number of entries in array */
#elif defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
uint16_t tag;
pb_type_t type;
uint8_t data_offset;
int8_t size_offset;
uint16_t data_size;
uint16_t array_size;
#else
uint32_t tag;
pb_type_t type;
uint8_t data_offset;
int8_t size_offset;
uint32_t data_size;
uint32_t array_size;
#endif
/* Pointer to message structure for submessage
/* Field definitions for submessage
* OR default value for all other non-array, non-callback types
* If null, then field will zeroed. */
const void *ptr;
} pb_packed;
PB_PACKED_STRUCT_END
/* This structure is used for 'bytes' arrays.
* It has the number of bytes in the beginning, and after that an array.
* Note that actual structs used will have a different length of bytes array.
*/
typedef struct {
struct _pb_bytes_array_t {
size_t size;
uint8_t bytes[1];
} pb_bytes_array_t;
};
/* This structure is used for dynamically allocated 'bytes' arrays.
*/
typedef struct {
size_t size;
size_t alloced;
uint8_t *bytes;
} pb_bytes_t;
/* This macro defines the type of a structure for a message with N
* fields.
*/
#define PB_MSG_STRUCT(N) struct { \
unsigned int field_count; \
size_t size; \
pb_field_t fields[N]; \
}
/* This is the visible type for generated message structures.
* The actual number of fields at the end will vary by message type.
*/
typedef PB_MSG_STRUCT(1) pb_message_t;
/* These macros are used to manipulate the has_fields array in
* generated messages.
*/
#define PB_FIELD_INDEX(TYPE, FIELD) TYPE ## _ ## FIELD ## _index
#define PB_FIELD_BYTE(TYPE, FIELD) (PB_FIELD_INDEX(TYPE, FIELD) / 8)
#define PB_FIELD_MASK(TYPE, FIELD) (1 << (PB_FIELD_INDEX(TYPE, FIELD) & 7))
#define PB_HAS_FIELD(STRUCT, TYPE, FIELD) \
(((STRUCT).has_fields[PB_FIELD_BYTE(TYPE, FIELD)] \
& PB_FIELD_MASK(TYPE, FIELD)) != 0)
#define PB_SET_FIELD(STRUCT, TYPE, FIELD) \
((STRUCT).has_fields[PB_FIELD_BYTE(TYPE, FIELD)] \
|= PB_FIELD_MASK(TYPE, FIELD))
#define PB_CLEAR_FIELD(STRUCT, TYPE, FIELD) \
((STRUCT).has_fields[PB_FIELD_BYTE(TYPE, FIELD)] \
&= ~PB_FIELD_MASK(TYPE, FIELD))
typedef struct _pb_bytes_array_t pb_bytes_array_t;
/* This structure is used for giving the callback function.
* It is stored in the message structure and filled in by the method that
@@ -177,14 +201,21 @@ typedef PB_MSG_STRUCT(1) pb_message_t;
*/
typedef struct _pb_istream_t pb_istream_t;
typedef struct _pb_ostream_t pb_ostream_t;
typedef struct _pb_strstream_t pb_strstream_t;
typedef struct _pb_callback_t pb_callback_t;
struct _pb_callback_t {
#ifdef PB_OLD_CALLBACK_STYLE
/* Deprecated since nanopb-0.2.1 */
union {
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg);
bool (*encode_buffer)(pb_strstream_t *stream, const pb_field_t *field, const void *arg);
} funcs;
#else
/* New function signature, which allows modifying arg contents in callback. */
union {
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
} funcs;
#endif
/* Free arg for use by callback */
void *arg;
@@ -202,8 +233,101 @@ 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_LAST_FIELD {0,0,0,0}
#define pb_delta_end(st, m1, m2) (int)(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.
* Some code space can be saved by disabling the error
* messages if not used.
*/
#ifdef PB_NO_ERRMSG
#define PB_RETURN_ERROR(stream,msg) return false
#define PB_GET_ERROR(stream) "(errmsg disabled)"
#else
#define PB_RETURN_ERROR(stream,msg) \
do {\
if ((stream)->errmsg == NULL) \
(stream)->errmsg = (msg); \
return false; \
} while(0)
#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c.
* The main function is pb_decode. You will also need to create an input
* stream, which is easiest to do with pb_istream_t.
* stream, which is easiest to do with pb_istream_from_buffer().
*
* You also need structures and their corresponding pb_field_t descriptions.
* These are usually generated from .proto-files with a script.
@@ -12,6 +12,10 @@
#include <stdbool.h>
#include "pb.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Lightweight input stream.
* You can provide a callback function for reading or use
* pb_istream_from_buffer.
@@ -19,20 +23,31 @@
* Rules for callback:
* 1) Return false on IO errors. This will cause decoding to abort.
*
* 2) If buf is NULL, read but don't store bytes ("skip input").
*
* 3) You can use state to store your own data (e.g. buffer pointer),
* 2) You can use state to store your own data (e.g. buffer pointer),
* and rely on pb_read to verify that no-body reads past bytes_left.
*
* 4) Your callback may be used with substreams, in which case bytes_left
* 3) Your callback may be used with substreams, in which case bytes_left
* is different than from the main stream. Don't use bytes_left to compute
* any pointers.
*/
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;
#ifndef PB_NO_ERRMSG
const char *errmsg;
#endif
};
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize);
@@ -40,25 +55,56 @@ bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count);
/* Decode from stream to destination struct.
* Returns true on success, false on any failure.
* The actual struct pointed to by dest must match the description in msg.
* The actual struct pointed to by dest must match the description in fields.
*/
bool pb_decode(pb_istream_t *stream, const pb_message_t *msg, void *dest_struct);
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
/* Same as pb_decode, except does not initialize the destination structure
* to default values. This is slightly faster if you need no default values
* and just do memset(struct, 0, sizeof(struct)) yourself.
*
* It can also be used to merge fields from a new message into a previously
* initialized structure.
*/
bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
/* --- Helper functions ---
* You may want to use these from your caller or callbacks.
*/
/* Decode the tag for the next field in the stream. Gives the wire type and
* field tag. At end of the message, returns false and sets eof to true. */
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
/* Skip the field payload data, given the wire type. */
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
/* Decode an integer in the varint format. This works for bool, enum, int32,
* int64, uint32 and uint64 field types. */
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
bool pb_skip_varint(pb_istream_t *stream);
bool pb_skip_string(pb_istream_t *stream);
/* Decode an integer in the zig-zagged svarint format. This works for sint32
* and sint64. */
bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
/* --- Field decoders ---
* Each decoder takes stream and field description, and a pointer to the field
* in the destination struct (dest = struct_addr + field->data_offset).
* For arrays, these functions are called repeatedly.
/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
* a 4-byte wide C variable. */
bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
* a 8-byte wide C variable. */
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
/* Make a limited-length substream for reading a PB_WT_STRING field. */
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
/* --- Internal functions ---
* These functions are not terribly useful for the average library user, but
* are exported to make the unit testing and extending nanopb easier.
*/
#ifdef NANOPB_INTERNALS
bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
@@ -68,10 +114,12 @@ bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
/* Release memory and clear pointers for any unused elements of
* dest_struct. This function is only compiled when MALLOC_HEADER is
* defined for pb_decode.c.
*/
bool pb_clean(const pb_message_t *msg, void *dest_struct);
bool pb_skip_varint(pb_istream_t *stream);
bool pb_skip_string(pb_istream_t *stream);
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -3,18 +3,18 @@
* 2011 Petteri Aimonen <jpa@kapsi.fi>
*/
#define NANOPB_INTERNALS
#include "pb.h"
#include "pb_encode.h"
#include <string.h>
#ifdef __GNUC__
/* Verify that we remember to check all return values for proper error propagation */
#define checkreturn __attribute__((warn_unused_result))
/* The warn_unused_result attribute appeared first in gcc-3.4.0 */
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
#define checkreturn
#else
#define checkreturn
/* Verify that we remember to check all return values for proper error propagation */
#define checkreturn __attribute__((warn_unused_result))
#endif
typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
/* --- Function pointers to field encoders ---
@@ -36,18 +36,28 @@ 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;
#ifndef PB_NO_ERRMSG
stream.errmsg = NULL;
#endif
return stream;
}
@@ -56,10 +66,15 @@ bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count
if (stream->callback != NULL)
{
if (stream->bytes_written + count > stream->max_size)
return false;
PB_RETURN_ERROR(stream, "stream full");
#ifdef PB_BUFFER_ONLY
if (!buf_write(stream, buf, count))
PB_RETURN_ERROR(stream, "io error");
#else
if (!stream->callback(stream, buf, count))
return false;
PB_RETURN_ERROR(stream, "io error");
#endif
}
stream->bytes_written += count;
@@ -75,7 +90,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,
const void *pData, size_t count, pb_encoder_t func)
{
int i;
size_t i;
const void *p;
size_t size;
@@ -98,7 +113,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
}
else
{
pb_ostream_t sizestream = {0};
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
p = pData;
for (i = 0; i < count; i++)
{
@@ -109,7 +124,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
size = sizestream.bytes_written;
}
if (!pb_encode_varint(stream, size))
if (!pb_encode_varint(stream, (uint64_t)size))
return false;
if (stream->callback == NULL)
@@ -140,37 +155,16 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
return true;
}
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_message_t *msg, const void *src_struct)
bool checkreturn encode_static_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData)
{
const void *pData = src_struct;
pb_encoder_t func;
const void *pSize;
const char *has_fields = src_struct;
unsigned int i;
size_t prev_size = 0;
for (i = 0; i < msg->field_count; i++)
{
const pb_field_t *field = &msg->fields[i];
pb_encoder_t func = PB_ENCODERS[PB_LTYPE(field->type)];
pData = (const char*)pData + prev_size + field->data_offset;
func = PB_ENCODERS[PB_LTYPE(field->type)];
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))
{
case PB_HTYPE_OPTIONAL:
if (!(has_fields[i/8] & (1 << i%8)))
break;
if (PB_POINTER(field->type)
&& (PB_LTYPE(field->type) == PB_LTYPE_STRING
|| PB_LTYPE(field->type) == PB_LTYPE_SUBMESSAGE)
&& *(void**)pData == NULL)
break;
/* else fall through to required case */
case PB_HTYPE_REQUIRED:
if (!pb_encode_tag_for_field(stream, field))
return false;
@@ -178,22 +172,82 @@ bool checkreturn pb_encode(pb_ostream_t *stream, const pb_message_t *msg, const
return false;
break;
case PB_HTYPE_ARRAY:
if (!encode_array(stream, field, pData, *(size_t*)pSize, func))
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;
case PB_HTYPE_CALLBACK:
{
pb_callback_t *callback = (pb_callback_t*)pData;
default:
PB_RETURN_ERROR(stream, "invalid field type");
}
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;
#ifdef PB_OLD_CALLBACK_STYLE
const void *arg = callback->arg;
#else
void * const *arg = &(callback->arg);
#endif
if (callback->funcs.encode != NULL)
{
if (!callback->funcs.encode(stream, field, callback->arg))
if (!callback->funcs.encode(stream, field, arg))
PB_RETURN_ERROR(stream, "callback error");
}
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;
size_t prev_size = 0;
while (field->tag != 0)
{
pData = (const char*)pData + prev_size + field->data_offset;
prev_size = field->data_size;
/* Special case for static arrays */
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
{
prev_size *= field->array_size;
}
switch (PB_ATYPE(field->type))
{
case PB_ATYPE_STATIC:
if (!encode_static_field(stream, field, pData))
return false;
}
break;
case PB_ATYPE_CALLBACK:
if (!encode_callback_field(stream, field, pData))
return false;
break;
default:
PB_RETURN_ERROR(stream, "invalid field type");
}
}
field++;
}
return true;
@@ -203,14 +257,14 @@ bool checkreturn pb_encode(pb_ostream_t *stream, const pb_message_t *msg, const
bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
{
uint8_t buffer[10];
int i = 0;
size_t i = 0;
if (value == 0)
return pb_write(stream, (uint8_t*)&value, 1);
while (value)
{
buffer[i] = (value & 0x7F) | 0x80;
buffer[i] = (uint8_t)((value & 0x7F) | 0x80);
value >>= 7;
i++;
}
@@ -219,9 +273,54 @@ bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
return pb_write(stream, buffer, i);
}
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number)
bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
{
int tag = wiretype | (field_number << 3);
uint64_t zigzagged;
if (value < 0)
zigzagged = (uint64_t)(~(value << 1));
else
zigzagged = (uint64_t)(value << 1);
return pb_encode_varint(stream, zigzagged);
}
bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
{
#ifdef __BIG_ENDIAN__
const uint8_t *bytes = value;
uint8_t lebytes[4];
lebytes[0] = bytes[3];
lebytes[1] = bytes[2];
lebytes[2] = bytes[1];
lebytes[3] = bytes[0];
return pb_write(stream, lebytes, 4);
#else
return pb_write(stream, (const uint8_t*)value, 4);
#endif
}
bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
{
#ifdef __BIG_ENDIAN__
const uint8_t *bytes = value;
uint8_t lebytes[8];
lebytes[0] = bytes[7];
lebytes[1] = bytes[6];
lebytes[2] = bytes[5];
lebytes[3] = bytes[4];
lebytes[4] = bytes[3];
lebytes[5] = bytes[2];
lebytes[6] = bytes[1];
lebytes[7] = bytes[0];
return pb_write(stream, lebytes, 8);
#else
return pb_write(stream, (const uint8_t*)value, 8);
#endif
}
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
{
uint64_t tag = wiretype | (field_number << 3);
return pb_encode_varint(stream, tag);
}
@@ -250,7 +349,7 @@ bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t
break;
default:
return false;
PB_RETURN_ERROR(stream, "invalid field type");
}
return pb_encode_tag(stream, wiretype, field->tag);
@@ -258,126 +357,32 @@ bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t
bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size)
{
if (!pb_encode_varint(stream, size))
if (!pb_encode_varint(stream, (uint64_t)size))
return false;
return pb_write(stream, buffer, size);
}
/* Field encoders */
/* 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)
bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
{
#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)
{
if ((field != NULL) && PB_POINTER(field->type)) {
pb_bytes_t *bytes = (pb_bytes_t*)src;
return pb_encode_string(stream, bytes->bytes, bytes->size);
} else {
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)
{
size_t len;
if ((field != NULL) && PB_POINTER(field->type))
src = *(char**)src;
len = src ? strlen((char*)src) : 0;
return pb_encode_string(stream, (uint8_t*)src, len);
}
bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
pb_ostream_t substream = {0};
/* First calculate the message size using a non-writing substream. */
pb_ostream_t substream = PB_OSTREAM_SIZING;
size_t size;
bool status;
if (field->ptr == NULL)
return false;
if (PB_POINTER(field->type)) {
src = *(void**)src;
if (src == NULL)
return false;
}
if (!pb_encode(&substream, (const pb_message_t*)field->ptr, src))
if (!pb_encode(&substream, fields, src_struct))
return false;
size = substream.bytes_written;
if (!pb_encode_varint(stream, size))
if (!pb_encode_varint(stream, (uint64_t)size))
return false;
if (stream->callback == NULL)
return pb_write(stream, NULL, size); /* Just sizing */
if (stream->bytes_written + size > stream->max_size)
return false;
PB_RETURN_ERROR(stream, "stream full");
/* Use a substream to verify that a callback doesn't write more than
* what it did the first time. */
@@ -385,15 +390,86 @@ bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field
substream.state = stream->state;
substream.max_size = size;
substream.bytes_written = 0;
#ifndef PB_NO_ERRMSG
substream.errmsg = NULL;
#endif
status = pb_encode(&substream, (const pb_message_t*)field->ptr, src);
status = pb_encode(&substream, fields, src_struct);
stream->bytes_written += substream.bytes_written;
stream->state = substream.state;
#ifndef PB_NO_ERRMSG
stream->errmsg = substream.errmsg;
#endif
if (substream.bytes_written != size)
return false;
PB_RETURN_ERROR(stream, "submsg size changed");
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 = *(const uint8_t*)src; break;
case 2: value = *(const uint16_t*)src; break;
case 4: value = *(const uint32_t*)src; break;
case 8: value = *(const uint64_t*)src; break;
default: PB_RETURN_ERROR(stream, "invalid 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)
{
int64_t value = 0;
switch (field->data_size)
{
case 4: value = *(const int32_t*)src; break;
case 8: value = *(const int64_t*)src; break;
default: PB_RETURN_ERROR(stream, "invalid data_size");
}
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)
{
const pb_bytes_array_t *bytes = (const 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, (const uint8_t*)src, strlen((const char*)src));
}
bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
if (field->ptr == NULL)
PB_RETURN_ERROR(stream, "invalid field descriptor");
return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
}

View File

@@ -9,6 +9,10 @@
#include <stdbool.h>
#include "pb.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Lightweight output stream.
* You can provide callback for writing or use pb_ostream_from_buffer.
*
@@ -28,45 +32,100 @@
*/
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;
#ifndef PB_NO_ERRMSG
const char *errmsg;
#endif
};
pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize);
bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count);
/* Stream type for use in computing message sizes */
#ifndef PB_NO_ERRMSG
#define PB_OSTREAM_SIZING {0,0,0,0,0}
#else
#define PB_OSTREAM_SIZING {0,0,0,0}
#endif
/* Encode struct to given output stream.
* Returns true on success, false on any failure.
* The actual struct pointed to by src_struct must match the description in msg.
* The actual struct pointed to by src_struct must match the description in fields.
* All required fields in the struct are assumed to have been filled in.
*/
bool pb_encode(pb_ostream_t *stream, const pb_message_t *msg, const void *src_struct);
bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
/* --- Helper functions ---
* You may want to use these from your caller or callbacks.
*/
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number);
/* Encode tag based on LTYPE and field number defined in the field structure. */
/* Encode field header based on LTYPE and field number defined in the field structure.
* Call this from the callback before writing out field contents. */
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, uint32_t 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);
/* --- Field encoders ---
* Each encoder writes the content for the field.
* The tag/wire type has been written already.
/* Encode a fixed32, sfixed32 or float value.
* You need to pass a pointer to a 4-byte wide C variable. */
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_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_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_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);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -1,362 +0,0 @@
/* pb_encode_buffer.c - encode a protobuf to an in-memory array
*
* 2011 Michael Poole <mdpoole@troilus.org>
* Part of nanopb, 2011 Petteri Aimonen <jpa@kapsi.fi>
*/
#include "pb_encode_buffer.h"
#include <string.h>
#ifdef __GNUC__
/* Verify that we remember to check all return values for proper error propagation */
#define checkreturn __attribute__((warn_unused_result))
#else
#define checkreturn
#endif
typedef bool (*pb_encoder_t)(pb_strstream_t *stream, const pb_field_t *field, const void *src) checkreturn;
static bool pb_encb_varint(pb_strstream_t *stream, const pb_field_t *field, const void *src);
static bool pb_encb_svarint(pb_strstream_t *stream, const pb_field_t *field, const void *src);
static bool pb_encb_fixed32(pb_strstream_t *stream, const pb_field_t *field, const void *src);
static bool pb_encb_fixed64(pb_strstream_t *stream, const pb_field_t *field, const void *src);
static bool pb_encb_bytes(pb_strstream_t *stream, const pb_field_t *field, const void *src);
static bool pb_encb_string(pb_strstream_t *stream, const pb_field_t *field, const void *src);
static bool pb_encb_submessage(pb_strstream_t *stream, const pb_field_t *field, const void *src);
/* --- Function pointers to field encoders ---
* Order in the array must match pb_action_t LTYPE numbering.
*/
static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
&pb_encb_varint,
&pb_encb_svarint,
&pb_encb_fixed32,
&pb_encb_fixed64,
&pb_encb_bytes,
&pb_encb_string,
&pb_encb_submessage
};
/* pb_strstream_t implementation */
pb_strstream_t pb_strstream_from_buffer(uint8_t *buf, size_t bufsize)
{
pb_strstream_t stream;
stream.buffer = buf;
stream.last = buf + bufsize;
return stream;
}
bool pb_buf_write(pb_strstream_t *stream, const uint8_t *buf, size_t count)
{
if (stream->buffer + count > stream->last)
return false;
stream->last -= count;
memcpy(stream->last, buf, count);
return true;
}
/* Main encoding stuff */
/* This function is static for the same reason as the version in
* pb_encode.c.
*/
static bool checkreturn encode_array(pb_strstream_t *stream, const pb_field_t *field,
const void *pData, size_t count, pb_encoder_t func)
{
const void *p;
uint8_t *start;
size_t size;
int i;
if (count == 0)
return true;
start = stream->last;
p = (const char*)pData + field->data_size * count;
if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
{
/* Write the data (in reverse order). */
for (i = 0; i < count; i++)
{
p = (const char*)p - field->data_size;
if (!func(stream, field, p))
return false;
}
/* Write the size. */
size = start - stream->last;
if (!pb_encbuf_varint(stream, size))
return false;
if (!pb_encbuf_tag(stream, PB_WT_STRING, field->tag))
return false;
}
else
{
for (i = 0; i < count; i++)
{
p = (const char*)p - field->data_size;
if (!func(stream, field, p))
return false;
if (!pb_encbuf_tag_for_field(stream, field))
return false;
}
}
return true;
}
bool checkreturn pb_encode_buffer(pb_strstream_t *stream, const pb_message_t *msg, const void *src_struct)
{
const char *has_fields = src_struct;
const uint8_t *pData = src_struct;
const pb_field_t *field;
pb_encoder_t func;
size_t size;
unsigned int i;
/* msg->size includes trailing padding, so we must calculate the
* offset of the last field by counting forward from the start.
*/
for (i = 0; i < msg->field_count; i++)
{
field = &msg->fields[i];
pData += field->data_offset;
size = field->data_size;
if (PB_HTYPE(field->type) == PB_HTYPE_ARRAY)
size *= field->array_size;
pData += size;
}
/* Iterate through the fields in reverse order. Because we write
* from the end of the buffer, the result is in canonical order.
*/
for (i = msg->field_count; i > 0; pData -= field->data_offset)
{
field = &msg->fields[--i];
func = PB_ENCODERS[PB_LTYPE(field->type)];
size = field->data_size;
if (PB_HTYPE(field->type) == PB_HTYPE_ARRAY)
size *= field->array_size;
pData -= size;
switch (PB_HTYPE(field->type))
{
case PB_HTYPE_OPTIONAL:
if (!(has_fields[i/8] & (1 << i%8)))
break;
if (PB_POINTER(field->type)
&& (PB_LTYPE(field->type) == PB_LTYPE_STRING
|| PB_LTYPE(field->type) == PB_LTYPE_SUBMESSAGE)
&& *(void**)pData == NULL)
break;
/* else fall through to required case */
case PB_HTYPE_REQUIRED:
if (!func(stream, field, pData))
return false;
if (!pb_encbuf_tag_for_field(stream, field))
return false;
break;
case PB_HTYPE_ARRAY:
size = *(size_t*)(pData + field->size_offset);
if (!encode_array(stream, field, pData, size, func))
return false;
break;
case PB_HTYPE_CALLBACK:
{
pb_callback_t *callback = (pb_callback_t*)pData;
if (callback->funcs.encode_buffer != NULL
&& !callback->funcs.encode_buffer(stream, field, callback->arg))
return false;
break;
}
}
}
return true;
}
/* Helper functions */
bool checkreturn pb_encbuf_varint(pb_strstream_t *stream, uint64_t value)
{
if (stream->last == stream->buffer)
{
return false;
}
else if (value < 128)
{
*--stream->last = value;
return true;
}
else
{
uint8_t buffer[10];
int i = 0;
while (value)
{
buffer[i] = (value & 0x7F) | 0x80;
value >>= 7;
i++;
}
buffer[i-1] &= 0x7F;
return pb_buf_write(stream, buffer, i);
}
}
bool checkreturn pb_encbuf_tag(pb_strstream_t *stream, pb_wire_type_t wiretype, int field_number)
{
int tag = wiretype | (field_number << 3);
return pb_encbuf_varint(stream, tag);
}
bool checkreturn pb_encbuf_tag_for_field(pb_strstream_t *stream, const pb_field_t *field)
{
pb_wire_type_t wiretype;
switch (PB_LTYPE(field->type))
{
case PB_LTYPE_VARINT:
case PB_LTYPE_SVARINT:
wiretype = PB_WT_VARINT;
break;
case PB_LTYPE_FIXED32:
wiretype = PB_WT_32BIT;
break;
case PB_LTYPE_FIXED64:
wiretype = PB_WT_64BIT;
break;
case PB_LTYPE_BYTES:
case PB_LTYPE_STRING:
case PB_LTYPE_SUBMESSAGE:
wiretype = PB_WT_STRING;
break;
default:
return false;
}
return pb_encbuf_tag(stream, wiretype, field->tag);
}
bool checkreturn pb_encbuf_string(pb_strstream_t *stream, const uint8_t *buffer, size_t size)
{
if (!pb_buf_write(stream, buffer, size))
return false;
return pb_encbuf_varint(stream, size);
}
/* Field encoders */
/* Copy srcsize bytes of integer 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(uint64_t *dest, const void *src, size_t srcsize)
{
#ifdef __BIG_ENDIAN__
memcpy((char*)dest + sizeof(*dest) - srcsize, src, srcsize);
#else
memcpy(dest, src, srcsize);
#endif
}
static bool pb_encb_varint(pb_strstream_t *stream, const pb_field_t *field, const void *src)
{
uint64_t value = 0;
endian_copy(&value, src, field->data_size);
return pb_encbuf_varint(stream, value);
}
static bool pb_encb_svarint(pb_strstream_t *stream, const pb_field_t *field, const void *src)
{
uint64_t value = 0;
uint64_t zigzagged;
uint64_t signbitmask = (uint64_t)0x80 << (field->data_size * 8 - 8);
uint64_t xormask = ((uint64_t)-1) >> (64 - field->data_size * 8);
endian_copy(&value, src, field->data_size);
if (value & signbitmask)
zigzagged = ((value ^ xormask) << 1) | 1;
else
zigzagged = value << 1;
return pb_encbuf_varint(stream, zigzagged);
}
static bool pb_encb_fixed32(pb_strstream_t *stream, const pb_field_t *field, const void *src)
{
#ifdef __BIG_ENDIAN__
const uint8_t *bytes = (const uint8_t*)src;
uint8_t lebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
src = lebytes;
#endif
return pb_buf_write(stream, src, 4);
}
static bool pb_encb_fixed64(pb_strstream_t *stream, const pb_field_t *field, const void *src)
{
#ifdef __BIG_ENDIAN__
const uint8_t *bytes = (const uint8_t*)src;
uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4],
bytes[3], bytes[2], bytes[1], bytes[0]};
src = lebytes;
#endif
return pb_buf_write(stream, src, 8);
}
static bool pb_encb_bytes(pb_strstream_t *stream, const pb_field_t *field, const void *src)
{
if ((field != NULL) && PB_POINTER(field->type)) {
pb_bytes_t *bytes = (pb_bytes_t*)src;
return pb_encbuf_string(stream, bytes->bytes, bytes->size);
} else {
pb_bytes_array_t *bytes = (pb_bytes_array_t*)src;
return pb_encbuf_string(stream, bytes->bytes, bytes->size);
}
}
static bool pb_encb_string(pb_strstream_t *stream, const pb_field_t *field, const void *src)
{
size_t len;
if ((field != NULL) && PB_POINTER(field->type))
src = *(char**)src;
len = src ? strlen((char*)src) : 0;
return pb_encbuf_string(stream, (uint8_t*)src, len);
}
static bool pb_encb_submessage(pb_strstream_t *stream, const pb_field_t *field, const void *src)
{
uint8_t *start;
size_t size;
if (field->ptr == NULL)
return false;
if (PB_POINTER(field->type)) {
src = *(void**)src;
if (src == NULL)
return false;
}
start = stream->last;
if (!pb_encode_buffer(stream, (const pb_message_t*)field->ptr, src))
return false;
size = start - stream->last;
if (!pb_encbuf_varint(stream, size))
return false;
return true;
}

View File

@@ -1,43 +0,0 @@
#ifndef _PB_ENCODE_BUFFER_H_
#define _PB_ENCODE_BUFFER_H_
/* pb_encode_buffer.h: Functions to encode protocol buffers to an
* in-memory array of bytes. Depends on pb_encode_buffer.c. The main
* function is pb_encode_buffer. You also need an output buffer,
* structures and their field descriptions (just like with pb_decode
* or pb_encode).
*/
#include <stdbool.h>
#include "pb.h"
/* Output stream for an in-memory buffer.
*/
struct _pb_strstream_t
{
uint8_t *buffer;
uint8_t *last;
};
pb_strstream_t pb_strstream_from_buffer(uint8_t *buf, size_t bufsize);
bool pb_buf_write(pb_strstream_t *stream, const uint8_t *buf, size_t count);
/* --- Helper functions ---
* You may want to use these from your caller or callbacks.
*/
bool pb_encbuf_varint(pb_strstream_t *stream, uint64_t value);
bool pb_encbuf_tag(pb_strstream_t *stream, pb_wire_type_t wiretype, int field_number);
/* Encode tag based on LTYPE and field number defined in the field structure. */
bool pb_encbuf_tag_for_field(pb_strstream_t *stream, const pb_field_t *field);
/* Write length as varint and then the contents of buffer. */
bool pb_encbuf_string(pb_strstream_t *stream, const uint8_t *buffer, size_t size);
/* Encode struct to given output stream.
* Returns true on success, false on any failure.
* The actual struct pointed to by src_struct must match the description in msg.
* All required fields in the struct are assumed to have been filled in.
*/
bool pb_encode_buffer(pb_strstream_t *stream, const pb_message_t *msg, const void *src_struct);
#endif

View File

@@ -1,38 +1,89 @@
CFLAGS=-ansi -Wall -Werror -I .. -g -O0 --coverage
LDFLAGS=--coverage
DEPS=../pb_decode.h ../pb_encode.h ../pb_encode_buffer.h ../pb.h person.pb.h callbacks.pb.h unittests.h unittestproto.pb.h
TESTS=test_decode1 test_encode1 test_decode_callbacks test_encode_callbacks decode_unittests decode_ptr_unittests encode_unittests
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
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 -fstack-protector-all
LDFLAGS+=--coverage
endif
ifneq (,$(findstring clang,$(CC_VERSION)))
CFLAGS_CORE=-pedantic -Wextra -Wcast-qual -Wconversion
endif
# Also use mudflap if it is available
# To enable, run with make -B USE_MUDFLAP=y
USE_MUDFLAP ?= n
ifeq ($(USE_MUDFLAP),y)
CFLAGS += -fmudflap
LDFLAGS += -lmudflap -fmudflap
endif
all: breakpoints $(TESTS) run_unittests
clean:
rm -f $(TESTS) *.pb.c *.pb.h *.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 $@ $<
%.o: %.c
%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
pb_encode.o: ../pb_encode.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
pb_encode_buffer.o: ../pb_encode_buffer.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
$(CC) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
pb_decode.o: ../pb_decode.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
pb_ptr_decode.o: ../pb_decode.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
decode_ptr_unittests.o: decode_unittests.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
$(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
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
pb_ptr_decode.o decode_ptr_unittests.o: CFLAGS += -DMALLOC_HEADER="<stdlib.h>"
decode_ptr_unittests: decode_ptr_unittests.o pb_ptr_decode.o unittestproto.pb.o
encode_unittests: encode_unittests.o pb_encode.o pb_encode_buffer.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$@ $<
@@ -45,15 +96,12 @@ breakpoints: ../*.c *.c
coverage: run_unittests
gcov pb_encode.gcda
gcov pb_encode_buffer.gcda
gcov pb_decode.gcda
gcov pb_ptr_decode.gcda
run_unittests: decode_unittests decode_ptr_unittests encode_unittests test_encode1 test_encode2 test_decode1 test_decode2 test_encode_callbacks test_decode_callbacks
run_unittests: $(TESTS)
rm -f *.gcda
./decode_unittests > /dev/null
./decode_ptr_unittests > /dev/null
./encode_unittests > /dev/null
[ "`./test_encode1 | ./test_decode1`" = \
@@ -65,8 +113,27 @@ run_unittests: decode_unittests decode_ptr_unittests encode_unittests test_encod
[ "`./test_encode2 | ./test_decode2`" = \
"`./test_encode2 | protoc --decode=Person -I. -I../generator -I/usr/include person.proto`" ]
[ "`./test_decode2 < person_with_extra_field.pb`" = \
"`./test_encode2 | ./test_decode2`" ]
[ "`./test_encode_callbacks | ./test_decode_callbacks`" = \
"`./test_encode_callbacks | protoc --decode=TestMessage callbacks.proto`" ]
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'
./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 options.pb.o
cat options.expected | while read -r p; do \
if ! grep -q "$$p" $<; then \
echo Expected: "$$p"; \
exit 1; \
fi \
done
run_fuzztest: test_decode3
bash -c 'ulimit -c unlimited; I=1; while true; do cat /dev/urandom | ./test_decode3 > /dev/null; I=$$(($$I+1)); echo -en "\r$$I"; done'

3
tests/alltypes.options Normal file
View File

@@ -0,0 +1,3 @@
* max_size:16
* max_count:5

90
tests/alltypes.proto Normal file
View File

@@ -0,0 +1,90 @@
message SubMessage {
required string substuff1 = 1 [default = "1"];
required int32 substuff2 = 2 [default = 2];
optional fixed32 substuff3 = 3 [default = 3];
}
message EmptyMessage {
}
enum MyEnum {
Zero = 0;
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;
required bytes req_bytes = 15;
required SubMessage req_submsg = 16;
required MyEnum req_enum = 17;
required EmptyMessage req_emptymsg = 18;
repeated int32 rep_int32 = 21;
repeated int64 rep_int64 = 22;
repeated uint32 rep_uint32 = 23;
repeated uint64 rep_uint64 = 24;
repeated sint32 rep_sint32 = 25;
repeated sint64 rep_sint64 = 26;
repeated bool rep_bool = 27;
repeated fixed32 rep_fixed32 = 28;
repeated sfixed32 rep_sfixed32= 29;
repeated float rep_float = 30;
repeated fixed64 rep_fixed64 = 31;
repeated sfixed64 rep_sfixed64= 32;
repeated double rep_double = 33;
repeated string rep_string = 34;
repeated bytes rep_bytes = 35;
repeated SubMessage rep_submsg = 36;
repeated MyEnum rep_enum = 37;
repeated EmptyMessage rep_emptymsg = 38;
optional int32 opt_int32 = 41 [default = 4041];
optional int64 opt_int64 = 42 [default = 4042];
optional uint32 opt_uint32 = 43 [default = 4043];
optional uint64 opt_uint64 = 44 [default = 4044];
optional sint32 opt_sint32 = 45 [default = 4045];
optional sint64 opt_sint64 = 46 [default = 4046];
optional bool opt_bool = 47 [default = false];
optional fixed32 opt_fixed32 = 48 [default = 4048];
optional sfixed32 opt_sfixed32= 49 [default = 4049];
optional float opt_float = 50 [default = 4050];
optional fixed64 opt_fixed64 = 51 [default = 4051];
optional sfixed64 opt_sfixed64= 52 [default = 4052];
optional double opt_double = 53 [default = 4053];
optional string opt_string = 54 [default = "4054"];
optional bytes opt_bytes = 55 [default = "4055"];
optional SubMessage opt_submsg = 56;
optional MyEnum opt_enum = 57 [default = Second];
optional EmptyMessage opt_emptymsg = 58;
// 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;
}

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 "bc_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

@@ -1,3 +1,5 @@
#define NANOPB_INTERNALS
#include <stdio.h>
#include <string.h>
#include "pb_decode.h"
@@ -17,11 +19,11 @@ bool stream_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
}
/* Verifies that the stream passed to callback matches the byte array pointed to by arg. */
bool callback_check(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool callback_check(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
int i;
uint8_t byte;
pb_bytes_array_t *ref = (pb_bytes_array_t*) arg;
pb_bytes_array_t *ref = (pb_bytes_array_t*) *arg;
for (i = 0; i < ref->size; i++)
{
@@ -167,14 +169,22 @@ int main()
{
pb_istream_t s;
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")
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("\x06xxxxxx"), !pb_dec_bytes(&s, &f, &d)))
TEST((s = S("\x05xxxxx"), pb_dec_bytes(&s, &f, &d) && d.size == 5))
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)))
}
{
@@ -193,13 +203,13 @@ int main()
IntegerArray dest;
COMMENT("Testing pb_decode with repeated int32 field")
TEST((s = S(""), pb_decode(&s, IntegerArray_msg, &dest) && dest.data_count == 0))
TEST((s = S("\x08\x01\x08\x02"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S(""), pb_decode(&s, IntegerArray_fields, &dest) && dest.data_count == 0))
TEST((s = S("\x08\x01\x08\x02"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 2 && dest.data[0] == 1 && dest.data[1] == 2))
s = S("\x08\x01\x08\x02\x08\x03\x08\x04\x08\x05\x08\x06\x08\x07\x08\x08\x08\x09\x08\x0A");
TEST(pb_decode(&s, IntegerArray_msg, &dest) && dest.data_count == 10 && dest.data[9] == 10)
TEST(pb_decode(&s, IntegerArray_fields, &dest) && dest.data_count == 10 && dest.data[9] == 10)
s = S("\x08\x01\x08\x02\x08\x03\x08\x04\x08\x05\x08\x06\x08\x07\x08\x08\x08\x09\x08\x0A\x08\x0B");
TEST(!pb_decode(&s, IntegerArray_msg, &dest))
TEST(!pb_decode(&s, IntegerArray_fields, &dest))
}
{
@@ -207,17 +217,17 @@ int main()
IntegerArray dest;
COMMENT("Testing pb_decode with packed int32 field")
TEST((s = S("\x0A\x00"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S("\x0A\x00"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 0))
TEST((s = S("\x0A\x01\x01"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S("\x0A\x01\x01"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 1 && dest.data[0] == 1))
TEST((s = S("\x0A\x0A\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S("\x0A\x0A\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 10 && dest.data[0] == 1 && dest.data[9] == 10))
TEST((s = S("\x0A\x0B\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"), !pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x0A\x0B\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"), !pb_decode(&s, IntegerArray_fields, &dest)))
/* Test invalid wire data */
TEST((s = S("\x0A\xFF"), !pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x0A\x01"), !pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x0A\xFF"), !pb_decode(&s, IntegerArray_fields, &dest)))
TEST((s = S("\x0A\x01"), !pb_decode(&s, IntegerArray_fields, &dest)))
}
{
@@ -225,14 +235,14 @@ int main()
IntegerArray dest;
COMMENT("Testing pb_decode with unknown fields")
TEST((s = S("\x18\x0F\x08\x01"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S("\x18\x0F\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 1 && dest.data[0] == 1))
TEST((s = S("\x19\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S("\x19\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 1 && dest.data[0] == 1))
TEST((s = S("\x1A\x00\x08\x01"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S("\x1A\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 1 && dest.data[0] == 1))
TEST((s = S("\x1B\x08\x01"), !pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x1D\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_msg, &dest)
TEST((s = S("\x1B\x08\x01"), !pb_decode(&s, IntegerArray_fields, &dest)))
TEST((s = S("\x1D\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
&& dest.data_count == 1 && dest.data[0] == 1))
}
@@ -246,25 +256,25 @@ int main()
COMMENT("Testing pb_decode with callbacks")
/* Single varint */
ref.size = 1; ref.bytes[0] = 0x55;
TEST((s = S("\x08\x55"), pb_decode(&s, CallbackArray_msg, &dest)))
TEST((s = S("\x08\x55"), pb_decode(&s, CallbackArray_fields, &dest)))
/* Packed varint */
ref.size = 3; ref.bytes[0] = ref.bytes[1] = ref.bytes[2] = 0x55;
TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_msg, &dest)))
TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_fields, &dest)))
/* Packed varint with loop */
ref.size = 1; ref.bytes[0] = 0x55;
TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_msg, &dest)))
TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_fields, &dest)))
/* Single fixed32 */
ref.size = 4; ref.bytes[0] = ref.bytes[1] = ref.bytes[2] = ref.bytes[3] = 0xAA;
TEST((s = S("\x0D\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_msg, &dest)))
TEST((s = S("\x0D\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_fields, &dest)))
/* Single fixed64 */
ref.size = 8; memset(ref.bytes, 0xAA, 8);
TEST((s = S("\x09\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_msg, &dest)))
TEST((s = S("\x09\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_fields, &dest)))
/* Unsupported field type */
TEST((s = S("\x0B\x00"), !pb_decode(&s, CallbackArray_msg, &dest)))
TEST((s = S("\x0B\x00"), !pb_decode(&s, CallbackArray_fields, &dest)))
/* Just make sure that our test function works */
ref.size = 1; ref.bytes[0] = 0x56;
TEST((s = S("\x08\x55"), !pb_decode(&s, CallbackArray_msg, &dest)))
TEST((s = S("\x08\x55"), !pb_decode(&s, CallbackArray_fields, &dest)))
}
{
@@ -272,39 +282,11 @@ int main()
IntegerArray dest;
COMMENT("Testing pb_decode message termination")
TEST((s = S(""), pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x00"), pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x08\x01"), pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x08\x01\x00"), pb_decode(&s, IntegerArray_msg, &dest)))
TEST((s = S("\x08"), !pb_decode(&s, IntegerArray_msg, &dest)))
}
{
pb_istream_t s;
PointerContainer dest;
COMMENT("Testing pb_decode with pointer fields")
memset(&dest, 0, sizeof(dest));
#ifdef MALLOC_HEADER
TEST((s = S("\x0A\x01\x61\x12\x01\x62\x1A\x00\x2A\x01\x65\x32\x01\x66"
"\x3A\x00\x42\x01\x63\x4A\x01\x64"),
pb_decode(&s, PointerContainer_msg, &dest)))
TEST(0 == strcmp(dest.text, "a"))
TEST(dest.blob.size == 1 && dest.blob.bytes[0] == 'b')
TEST(dest.submsg != NULL && dest.submsg->data == 10)
TEST(dest.rtext_count == 1 && (0 == strcmp(dest.rtext[0], "e")))
TEST(dest.rblob_count == 1 && dest.rblob[0].size == 1 &&
dest.rblob[0].bytes[0] == 'f')
TEST(dest.rsubmsg_count == 1)
TEST(0 == strcmp(dest.otext, "c"))
TEST(dest.oblob.size == 1 && dest.oblob.bytes[0] == 'd')
TEST(pb_clean(PointerContainer_msg, &dest));
#else
TEST((s = S("\x0A\x01\x61\x12\x01\x62\x2A\x01\x65\x32\x01\x66\x01\x66"
"\x3A\x00\x42\x01\x63\x4A\x01\x64"),
!pb_decode(&s, PointerContainer_msg, &dest)))
#endif
TEST((s = S(""), pb_decode(&s, IntegerArray_fields, &dest)))
TEST((s = S("\x00"), pb_decode(&s, IntegerArray_fields, &dest)))
TEST((s = S("\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)))
TEST((s = S("\x08\x01\x00"), pb_decode(&s, IntegerArray_fields, &dest)))
TEST((s = S("\x08"), !pb_decode(&s, IntegerArray_fields, &dest)))
}
if (status != 0)

View File

@@ -1,7 +1,8 @@
#define NANOPB_INTERNALS
#include <stdio.h>
#include <string.h>
#include "pb_encode.h"
#include "pb_encode_buffer.h"
#include "unittests.h"
#include "unittestproto.pb.h"
@@ -16,7 +17,7 @@ bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
return true;
}
bool fieldcallback(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
bool fieldcallback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
int value = 0x55;
if (!pb_encode_tag_for_field(stream, field))
@@ -24,15 +25,7 @@ bool fieldcallback(pb_ostream_t *stream, const pb_field_t *field, const void *ar
return pb_encode_varint(stream, value);
}
bool fieldcallback_buf(pb_strstream_t *stream, const pb_field_t *field, const void *arg)
{
int value = 0x55;
if (!pb_encbuf_varint(stream, value))
return false;
return pb_encbuf_tag_for_field(stream, field);
}
bool crazyfieldcallback(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
bool crazyfieldcallback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
/* This callback writes different amount of data the second time. */
uint32_t *state = (uint32_t*)arg;
@@ -42,16 +35,6 @@ bool crazyfieldcallback(pb_ostream_t *stream, const pb_field_t *field, const voi
return pb_encode_varint(stream, *state);
}
bool crazyfieldcallback_buf(pb_strstream_t *stream, const pb_field_t *field, const void *arg)
{
/* This callback writes different amount of data the second time. */
uint32_t *state = (uint32_t*)arg;
*state <<= 8;
if (!pb_encbuf_varint(stream, *state))
return false;
return pb_encbuf_tag_for_field(stream, field);
}
/* Check that expression x writes data y.
* Y is a string, which may contain null bytes. Null terminator is ignored.
*/
@@ -62,20 +45,6 @@ s = pb_ostream_from_buffer(buffer, sizeof(buffer)), \
memcmp(buffer, y, sizeof(y) - 1) == 0 && \
buffer[sizeof(y) - 1] == 0xAA
/* Check that expression x writes data y into s2.
* Y is a string, which may contain null bytes. Null terminator is ignored.
*/
#define WRITES_BUF(x, y) \
memset(buffer, 0xAA, sizeof(buffer)), \
s2 = pb_strstream_from_buffer(buffer, sizeof(buffer)), \
(x) && \
memcmp(s2.last, y, sizeof(y) - 1) == 0 && \
s2.last - buffer == sizeof(buffer) - (sizeof(y) - 1)
/* Check that expression x and y write data z into s and s2, respectively.
*/
#define WRITES_BOTH(x, y, z) (WRITES(x, z)) && (WRITES_BUF(y, z))
int main()
{
int status = 0;
@@ -105,81 +74,57 @@ int main()
{
uint8_t buffer[30];
pb_ostream_t s;
pb_strstream_t s2;
COMMENT("Test pb_encode_varint")
TEST(WRITES_BOTH(pb_encode_varint(&s, 0),
pb_encbuf_varint(&s2, 0), "\0"));
TEST(WRITES_BOTH(pb_encode_varint(&s, 1),
pb_encbuf_varint(&s2, 1), "\1"));
TEST(WRITES_BOTH(pb_encode_varint(&s, 0x7F),
pb_encbuf_varint(&s2, 0x7F), "\x7F"));
TEST(WRITES_BOTH(pb_encode_varint(&s, 0x80),
pb_encbuf_varint(&s2, 0x80), "\x80\x01"));
TEST(WRITES_BOTH(pb_encode_varint(&s, UINT32_MAX),
pb_encbuf_varint(&s2, UINT32_MAX),
"\xFF\xFF\xFF\xFF\x0F"));
TEST(WRITES_BOTH(pb_encode_varint(&s, UINT64_MAX),
pb_encbuf_varint(&s2, UINT64_MAX),
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"));
TEST(WRITES(pb_encode_varint(&s, 0), "\0"));
TEST(WRITES(pb_encode_varint(&s, 1), "\1"));
TEST(WRITES(pb_encode_varint(&s, 0x7F), "\x7F"));
TEST(WRITES(pb_encode_varint(&s, 0x80), "\x80\x01"));
TEST(WRITES(pb_encode_varint(&s, UINT32_MAX), "\xFF\xFF\xFF\xFF\x0F"));
TEST(WRITES(pb_encode_varint(&s, UINT64_MAX), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"));
}
{
uint8_t buffer[30];
pb_ostream_t s;
pb_strstream_t s2;
COMMENT("Test pb_encode_tag")
TEST(WRITES_BOTH(pb_encode_tag(&s, PB_WT_STRING, 5),
pb_encbuf_tag(&s2, PB_WT_STRING, 5), "\x2A"));
TEST(WRITES_BOTH(pb_encode_tag(&s, PB_WT_VARINT, 99),
pb_encbuf_tag(&s2, PB_WT_VARINT, 99), "\x98\x06"));
TEST(WRITES(pb_encode_tag(&s, PB_WT_STRING, 5), "\x2A"));
TEST(WRITES(pb_encode_tag(&s, PB_WT_VARINT, 99), "\x98\x06"));
}
{
uint8_t buffer[30];
pb_ostream_t s;
pb_strstream_t s2;
pb_field_t field = {10, PB_LTYPE_SVARINT};
COMMENT("Test pb_encode_tag_for_field")
TEST(WRITES_BOTH(pb_encode_tag_for_field(&s, &field),
pb_encbuf_tag_for_field(&s2, &field), "\x50"));
TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x50"));
field.type = PB_LTYPE_FIXED64;
TEST(WRITES_BOTH(pb_encode_tag_for_field(&s, &field),
pb_encbuf_tag_for_field(&s2, &field), "\x51"));
TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x51"));
field.type = PB_LTYPE_STRING;
TEST(WRITES_BOTH(pb_encode_tag_for_field(&s, &field),
pb_encbuf_tag_for_field(&s2, &field), "\x52"));
TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x52"));
field.type = PB_LTYPE_FIXED32;
TEST(WRITES_BOTH(pb_encode_tag_for_field(&s, &field),
pb_encbuf_tag_for_field(&s2, &field), "\x55"));
TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x55"));
}
{
uint8_t buffer[30];
pb_ostream_t s;
pb_strstream_t s2;
COMMENT("Test pb_encode_string")
TEST(WRITES_BOTH(pb_encode_string(&s, (const uint8_t*)"abcd", 4),
pb_encbuf_string(&s2, (const uint8_t*)"abcd", 4),
"\x04""abcd"));
TEST(WRITES_BOTH(pb_encode_string(&s, (const uint8_t*)"abcd\x00", 5),
pb_encbuf_string(&s2, (const uint8_t*)"abcd\x00", 5),
"\x05""abcd\x00"));
TEST(WRITES_BOTH(pb_encode_string(&s, (const uint8_t*)"", 0),
pb_encbuf_string(&s2, (const uint8_t*)"", 0), "\x00"));
TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"abcd", 4), "\x04""abcd"));
TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"abcd\x00", 5), "\x05""abcd\x00"));
TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"", 0), "\x00"));
}
{
uint8_t buffer[30];
pb_ostream_t s;
uint8_t value = 1;
int8_t svalue = -1;
int32_t max = INT32_MAX;
int32_t min = INT32_MIN;
int64_t lmax = INT64_MAX;
@@ -188,8 +133,6 @@ int main()
COMMENT("Test pb_enc_varint and pb_enc_svarint")
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);
TEST(WRITES(pb_enc_svarint(&s, &field, &max), "\xfe\xff\xff\xff\x0f"));
@@ -248,89 +191,70 @@ int main()
{
uint8_t buffer[10];
pb_ostream_t s;
pb_strstream_t s2;
IntegerArray msg = {{0}, 5, {1, 2, 3, 4, 5}};
IntegerArray msg = {5, {1, 2, 3, 4, 5}};
COMMENT("Test pb_encode with int32 array")
TEST(WRITES_BOTH(pb_encode(&s, IntegerArray_msg, &msg),
pb_encode_buffer(&s2, IntegerArray_msg, &msg),
"\x0A\x05\x01\x02\x03\x04\x05"))
TEST(WRITES(pb_encode(&s, IntegerArray_fields, &msg), "\x0A\x05\x01\x02\x03\x04\x05"))
msg.data_count = 0;
TEST(WRITES_BOTH(pb_encode(&s, IntegerArray_msg, &msg),
pb_encode_buffer(&s2, IntegerArray_msg, &msg), ""))
TEST(WRITES(pb_encode(&s, IntegerArray_fields, &msg), ""))
msg.data_count = 10;
TEST(!pb_encode(&s, IntegerArray_msg, &msg))
TEST(!pb_encode_buffer(&s2, IntegerArray_msg, &msg))
TEST(!pb_encode(&s, IntegerArray_fields, &msg))
}
{
uint8_t buffer[10];
pb_ostream_t s;
pb_strstream_t s2;
FloatArray msg = {{0}, 1, {99.0f}};
FloatArray msg = {1, {99.0f}};
COMMENT("Test pb_encode with float array")
TEST(WRITES_BOTH(pb_encode(&s, FloatArray_msg, &msg),
pb_encode_buffer(&s2, FloatArray_msg, &msg),
TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg),
"\x0A\x04\x00\x00\xc6\x42"))
msg.data_count = 0;
TEST(WRITES_BOTH(pb_encode(&s, FloatArray_msg, &msg),
pb_encode_buffer(&s2, FloatArray_msg, &msg), ""))
TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg), ""))
msg.data_count = 3;
TEST(!pb_encode(&s, FloatArray_msg, &msg))
TEST(!pb_encode_buffer(&s2, FloatArray_msg, &msg))
TEST(!pb_encode(&s, FloatArray_fields, &msg))
}
{
uint8_t buffer[10];
pb_ostream_t s;
pb_strstream_t s2;
CallbackArray msg;
COMMENT("Test pb_encode with callback field.")
msg.data.funcs.encode = &fieldcallback;
TEST(WRITES(pb_encode(&s, CallbackArray_msg, &msg), "\x08\x55"))
msg.data.funcs.encode_buffer = &fieldcallback_buf;
TEST(WRITES_BUF(pb_encode_buffer(&s2, CallbackArray_msg, &msg), "\x08\x55"))
COMMENT("Test pb_encode with callback field.")
TEST(WRITES(pb_encode(&s, CallbackArray_fields, &msg), "\x08\x55"))
}
{
uint8_t buffer[10];
pb_ostream_t s;
pb_strstream_t s2;
IntegerContainer msg = {{0}, {{0}, 5, {1,2,3,4,5}}};
IntegerContainer msg = {{5, {1,2,3,4,5}}};
COMMENT("Test pb_encode with packed array in a submessage.")
TEST(WRITES_BOTH(pb_encode(&s, IntegerContainer_msg, &msg),
pb_encode_buffer(&s2, IntegerContainer_msg, &msg),
TEST(WRITES(pb_encode(&s, IntegerContainer_fields, &msg),
"\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"))
}
{
uint8_t buffer[10];
pb_ostream_t s;
pb_strstream_t s2;
CallbackContainer msg;
CallbackContainerContainer msg2;
uint32_t state = 1;
COMMENT("Test pb_encode with callback field in a submessage.")
msg.submsg.data.funcs.encode = &fieldcallback;
msg2.submsg.submsg.data.funcs.encode = &fieldcallback;
TEST(WRITES(pb_encode(&s, CallbackContainer_msg, &msg), "\x0A\x02\x08\x55"))
TEST(WRITES(pb_encode(&s, CallbackContainerContainer_msg, &msg2),
"\x0A\x04\x0A\x02\x08\x55"))
msg.submsg.data.funcs.encode_buffer = &fieldcallback_buf;
msg2.submsg.submsg.data.funcs.encode_buffer = &fieldcallback_buf;
TEST(WRITES_BUF(pb_encode_buffer(&s2, CallbackContainer_msg, &msg), "\x0A\x02\x08\x55"))
TEST(WRITES_BUF(pb_encode_buffer(&s2, CallbackContainerContainer_msg, &msg2),
COMMENT("Test pb_encode with callback field in a submessage.")
TEST(WRITES(pb_encode(&s, CallbackContainer_fields, &msg), "\x0A\x02\x08\x55"))
TEST(WRITES(pb_encode(&s, CallbackContainerContainer_fields, &msg2),
"\x0A\x04\x0A\x02\x08\x55"))
/* Misbehaving callback: varying output between calls */
@@ -339,63 +263,9 @@ int main()
msg2.submsg.submsg.data.funcs.encode = &crazyfieldcallback;
msg2.submsg.submsg.data.arg = &state;
TEST(!pb_encode(&s, CallbackContainer_msg, &msg))
TEST(!pb_encode(&s, CallbackContainer_fields, &msg))
state = 1;
TEST(!pb_encode(&s, CallbackContainerContainer_msg, &msg2))
msg.submsg.data.funcs.encode_buffer = &crazyfieldcallback_buf;
msg.submsg.data.arg = &state;
msg2.submsg.submsg.data.funcs.encode_buffer = &crazyfieldcallback_buf;
msg2.submsg.submsg.data.arg = &state;
TEST(!pb_encode_buffer(&s2, CallbackContainer_msg, &msg))
state = 1;
TEST(!pb_encode_buffer(&s2, CallbackContainerContainer_msg, &msg2))
}
{
uint8_t buffer[30];
pb_ostream_t s;
pb_strstream_t s2;
PointerContainer msg;
DefaultContainer msg2;
IntegerArray msg3;
COMMENT("Test pb_encode with pointer fields.")
memset(&msg, 0, sizeof(msg));
memset(&msg2, 0, sizeof(msg2));
msg.text = "a";
msg.blob.size = 1;
msg.blob.bytes = (uint8_t*)"b";
msg.submsg = &msg2;
TEST(WRITES_BOTH(pb_encode(&s, PointerContainer_msg, &msg),
pb_encode_buffer(&s2, PointerContainer_msg, &msg),
"\x0A\x01\x61\x12\x01\x62\x1A\x00"))
memset(&msg3, 0, sizeof(msg3));
msg.rtext_count = 1;
msg.rtext[0] = "e";
msg.rblob_count = 1;
msg.rblob[0].size = 1;
msg.rblob[0].bytes = (uint8_t*)"f";
msg.rsubmsg_count = 1;
msg.rsubmsg[0] = &msg3;
TEST(WRITES_BOTH(pb_encode(&s, PointerContainer_msg, &msg),
pb_encode_buffer(&s2, PointerContainer_msg, &msg),
"\x0A\x01\x61\x12\x01\x62\x1A\x00"
"\x2A\x01\x65\x32\x01\x66\x3A\x00"));
PointerContainer_set(msg, otext);
msg.otext = "c";
PointerContainer_set(msg, oblob);
msg.oblob.size = 1;
msg.oblob.bytes = (uint8_t*)"d";
TEST(WRITES_BOTH(pb_encode(&s, PointerContainer_msg, &msg),
pb_encode_buffer(&s2, PointerContainer_msg, &msg),
"\x0A\x01\x61\x12\x01\x62\x1A\x00"
"\x2A\x01\x65\x32\x01\x66\x3A\x00"
"\x42\x01\x63\x4A\x01\x64"));
TEST(!pb_encode(&s, CallbackContainerContainer_fields, &msg2))
}
if (status != 0)

View File

138
tests/missing_fields.proto Normal file
View File

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

7
tests/no_messages.proto Normal file
View File

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

7
tests/options.expected Normal file
View File

@@ -0,0 +1,7 @@
char filesize\[20\];
char msgsize\[30\];
char fieldsize\[40\];
pb_callback_t int32_callback;
\sEnumValue1 = 1
Message5_EnumValue1
} pb_packed my_packed_struct;

73
tests/options.proto Normal file
View File

@@ -0,0 +1,73 @@
/* Test nanopb option parsing.
* options.expected lists the patterns that are searched for in the output.
*/
import "nanopb.proto";
// File level options
option (nanopb_fileopt).max_size = 20;
message Message1
{
required string filesize = 1;
}
// Message level options
message Message2
{
option (nanopb_msgopt).max_size = 30;
required string msgsize = 1;
}
// Field level options
message Message3
{
required string fieldsize = 1 [(nanopb).max_size = 40];
}
// Forced callback field
message Message4
{
required int32 int32_callback = 1 [(nanopb).type = FT_CALLBACK];
}
// Short enum names
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];
}

Binary file not shown.

View File

@@ -17,7 +17,7 @@ bool print_person(pb_istream_t *stream)
int i;
Person person;
if (!pb_decode(stream, Person_msg, &person))
if (!pb_decode(stream, Person_fields, &person))
return false;
/* Now the decoding is done, rest is just to print stuff out. */
@@ -25,7 +25,7 @@ bool print_person(pb_istream_t *stream)
printf("name: \"%s\"\n", person.name);
printf("id: %ld\n", (long)person.id);
if (Person_has(person, email))
if (person.has_email)
printf("email: \"%s\"\n", person.email);
for (i = 0; i < person.phone_count; i++)
@@ -34,7 +34,7 @@ bool print_person(pb_istream_t *stream)
printf("phone {\n");
printf(" number: \"%s\"\n", phone->number);
if (Person_PhoneNumber_has(*phone, type))
if (phone->has_type)
{
switch (phone->type)
{
@@ -63,13 +63,19 @@ int main()
uint8_t buffer[512];
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
if (!feof(stdin))
{
printf("Message does not fit in buffer\n");
return 1;
}
/* 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 (!print_person(&stream))
{
printf("Parsing failed.\n");
printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
return 1;
} else {
return 0;

View File

@@ -13,7 +13,7 @@ bool print_person(pb_istream_t *stream)
int i;
Person person;
if (!pb_decode(stream, Person_msg, &person))
if (!pb_decode(stream, Person_fields, &person))
return false;
/* Now the decoding is done, rest is just to print stuff out. */
@@ -21,7 +21,7 @@ bool print_person(pb_istream_t *stream)
printf("name: \"%s\"\n", person.name);
printf("id: %ld\n", (long)person.id);
if (Person_has(person, email))
if (person.has_email)
printf("email: \"%s\"\n", person.email);
for (i = 0; i < person.phone_count; i++)
@@ -30,7 +30,7 @@ bool print_person(pb_istream_t *stream)
printf("phone {\n");
printf(" number: \"%s\"\n", phone->number);
if (Person_PhoneNumber_has(*phone, type))
if (phone->has_type)
{
switch (phone->type)
{
@@ -59,13 +59,6 @@ bool callback(pb_istream_t *stream, uint8_t *buf, size_t count)
FILE *file = (FILE*)stream->state;
bool status;
if (buf == NULL)
{
/* Skipping data */
while (count-- && fgetc(file) != EOF);
return count == 0;
}
status = (fread(buf, 1, count, file) == count);
if (feof(file))
@@ -82,7 +75,7 @@ int main()
pb_istream_t stream = {&callback, stdin, 10000};
if (!print_person(&stream))
{
printf("Parsing failed.\n");
printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
return 1;
} else {
return 0;

197
tests/test_decode3.c Normal file
View File

@@ -0,0 +1,197 @@
/* Tests the decoding of all types.
* This is the counterpart of test_encode3.
* Run e.g. ./test_encode3 | ./test_decode3
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.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, 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);
TEST(alltypes.rep_emptymsg_count == 5);
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);
TEST(alltypes.has_opt_emptymsg == false);
}
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.has_opt_emptymsg == true);
}
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;
}
}

View File

@@ -6,7 +6,7 @@
#include <pb_decode.h>
#include "callbacks.pb.h"
bool print_string(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint8_t buffer[1024] = {0};
@@ -20,37 +20,37 @@ bool print_string(pb_istream_t *stream, const pb_field_t *field, void *arg)
/* Print the string, in format comparable with protoc --decode.
* Format comes from the arg defined in main().
*/
printf((char*)arg, buffer);
printf((char*)*arg, buffer);
return true;
}
bool print_int32(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool print_int32(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint64_t value;
if (!pb_decode_varint(stream, &value))
return false;
printf((char*)arg, (long)value);
printf((char*)*arg, (long)value);
return true;
}
bool print_fixed32(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool print_fixed32(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint32_t value;
if (!pb_dec_fixed32(stream, NULL, &value))
if (!pb_decode_fixed32(stream, &value))
return false;
printf((char*)arg, (long)value);
printf((char*)*arg, (long)value);
return true;
}
bool print_fixed64(pb_istream_t *stream, const pb_field_t *field, void *arg)
bool print_fixed64(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint64_t value;
if (!pb_dec_fixed64(stream, NULL, &value))
if (!pb_decode_fixed64(stream, &value))
return false;
printf((char*)arg, (long long)value);
printf((char*)*arg, (long long)value);
return true;
}
@@ -84,7 +84,7 @@ int main()
testmessage.fixed64value.funcs.decode = &print_fixed64;
testmessage.fixed64value.arg = "fixed64value: %lld\n";
if (!pb_decode(&stream, TestMessage_msg, &testmessage))
if (!pb_decode(&stream, TestMessage_fields, &testmessage))
return 1;
return 0;

View File

@@ -10,27 +10,24 @@
int main()
{
/* Initialize the structure with constants */
Person person = {{1 << Person_email_index},
"Test Person 99", 99, "test@person.com",
3, {{{1 << Person_PhoneNumber_type_index},
"555-12345678", Person_PhoneType_MOBILE},
{{1 << Person_PhoneNumber_type_index},
"99-2342", 0},
{{1 << Person_PhoneNumber_type_index},
"1234-5678", Person_PhoneType_WORK},
Person person = {"Test Person 99", 99, true, "test@person.com",
3, {{"555-12345678", true, Person_PhoneType_MOBILE},
{"99-2342", false, 0},
{"1234-5678", true, Person_PhoneType_WORK},
}};
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, Person_msg, &person))
if (pb_encode(&stream, Person_fields, &person))
{
fwrite(buffer, 1, stream.bytes_written, stdout);
return 0; /* Success */
}
else
{
fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1; /* Failure */
}
}

View File

@@ -15,22 +15,23 @@ bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
int main()
{
/* Initialize the structure with constants */
Person person = {{1 << Person_email_index},
"Test Person 99", 99, "test@person.com",
3, {{{1 << Person_PhoneNumber_type_index},
"555-12345678", Person_PhoneType_MOBILE},
{{1 << Person_PhoneNumber_type_index},
"99-2342", 0},
{{1 << Person_PhoneNumber_type_index},
"1234-5678", Person_PhoneType_WORK},
Person person = {"Test Person 99", 99, true, "test@person.com",
3, {{"555-12345678", true, Person_PhoneType_MOBILE},
{"99-2342", false, 0},
{"1234-5678", true, Person_PhoneType_WORK},
}};
/* Prepare the stream, output goes directly to stdout */
pb_ostream_t stream = {&streamcallback, stdout, SIZE_MAX, 0};
/* Now encode it and check if we succeeded. */
if (pb_encode(&stream, Person_msg, &person))
if (pb_encode(&stream, Person_fields, &person))
{
return 0; /* Success */
}
else
{
fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1; /* Failure */
}
}

130
tests/test_encode3.c Normal file
View File

@@ -0,0 +1,130 @@
/* Attempts to test all the datatypes supported by ProtoBuf.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pb_encode.h>
#include "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;
alltypes.rep_emptymsg_count = 5;
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.has_opt_emptymsg = true;
}
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: %s\n", PB_GET_ERROR(&stream));
return 1; /* Failure */
}
}

View File

@@ -5,7 +5,7 @@
#include <pb_encode.h>
#include "callbacks.pb.h"
bool encode_string(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
char *str = "Hello world!";
@@ -15,7 +15,7 @@ bool encode_string(pb_ostream_t *stream, const pb_field_t *field, const void *ar
return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}
bool encode_int32(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
bool encode_int32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
if (!pb_encode_tag_for_field(stream, field))
return false;
@@ -23,22 +23,22 @@ bool encode_int32(pb_ostream_t *stream, const pb_field_t *field, const void *arg
return pb_encode_varint(stream, 42);
}
bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
if (!pb_encode_tag_for_field(stream, field))
return false;
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, void * const *arg)
{
if (!pb_encode_tag_for_field(stream, field))
return false;
uint64_t value = 42;
return pb_enc_fixed64(stream, field, &value);
return pb_encode_fixed64(stream, &value);
}
int main()
@@ -52,13 +52,13 @@ int main()
testmessage.fixed32value.funcs.encode = &encode_fixed32;
testmessage.fixed64value.funcs.encode = &encode_fixed64;
TestMessage_set(testmessage, submsg);
testmessage.has_submsg = true;
testmessage.submsg.stringvalue.funcs.encode = &encode_string;
testmessage.submsg.int32value.funcs.encode = &encode_int32;
testmessage.submsg.fixed32value.funcs.encode = &encode_fixed32;
testmessage.submsg.fixed64value.funcs.encode = &encode_fixed64;
if (!pb_encode(&stream, TestMessage_msg, &testmessage))
if (!pb_encode(&stream, TestMessage_fields, &testmessage))
return 1;
if (fwrite(buffer, stream.bytes_written, 1, stdout) != 1)

View File

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

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

View File

@@ -26,28 +26,3 @@ message CallbackContainer {
message CallbackContainerContainer {
required CallbackContainer submsg = 1;
}
message DefaultContainer {
optional int32 data = 1 [default = 10];
}
message PointerContainer {
required string text = 1 [(nanopb).pointer = true];
required bytes blob = 2 [(nanopb).pointer = true];
required DefaultContainer submsg = 3 [(nanopb).pointer = true];
// This should be rejected:
// required int32 data = 4 [(nanopb).pointer = true];
repeated string rtext = 5 [(nanopb).pointer = true, (nanopb).max_count = 10];
repeated bytes rblob = 6 [(nanopb).pointer = true, (nanopb).max_count = 10];
repeated IntegerArray rsubmsg = 7 [(nanopb).pointer = true, (nanopb).max_count = 10];
optional string otext = 8 [(nanopb).pointer = true];
optional bytes oblob = 9 [(nanopb).pointer = true];
}
message RecursiveRef_A {
optional RecursiveRef_B submsg = 1 [(nanopb).pointer = true];
}
message RecursiveRef_B {
required RecursiveRef_A submsg = 1;
}

10
tools/set_version.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
# Run this from the top directory of nanopb tree.
# e.g. user@localhost:~/nanopb$ tools/set_version.sh nanopb-0.1.9-dev
# It sets the version number in pb.h and generator/nanopb_generator.py.
sed -i -e 's/nanopb_version\s*=\s*"[^"]*"/nanopb_version = "'$1'"/' generator/nanopb_generator.py
sed -i -e 's/#define\s*NANOPB_VERSION\s*.*/#define NANOPB_VERSION '$1'/' pb.h