Compare commits
25 Commits
nanopb-0.2
...
dev_no_pb_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17c10119dd | ||
|
|
97210c91a9 | ||
|
|
cace53dfbd | ||
|
|
eff9e11150 | ||
|
|
5813144246 | ||
|
|
171d64734a | ||
|
|
321ca6c1d5 | ||
|
|
935a26ab1c | ||
|
|
d7af99434a | ||
|
|
564bdc8448 | ||
|
|
dcdd7f98fd | ||
|
|
287207841d | ||
|
|
0074deba9a | ||
|
|
4d69cc2f3e | ||
|
|
c7b4ce0293 | ||
|
|
cd3af3272d | ||
|
|
ed564186e1 | ||
|
|
86d6983156 | ||
|
|
0848255d4a | ||
|
|
51f0e47295 | ||
|
|
49bd3f35a0 | ||
|
|
2bfd497eea | ||
|
|
e83fbd18d3 | ||
|
|
388d4de833 | ||
|
|
2363af29a2 |
19
.gitignore
vendored
19
.gitignore
vendored
@@ -5,17 +5,22 @@
|
|||||||
*.pb.c
|
*.pb.c
|
||||||
*.pb.h
|
*.pb.h
|
||||||
*.pb
|
*.pb
|
||||||
|
*.pyc
|
||||||
*~
|
*~
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
.sconsign.dblite
|
.sconsign.dblite
|
||||||
|
config.log
|
||||||
|
.sconf_temp
|
||||||
|
tests/build
|
||||||
julkaisu.txt
|
julkaisu.txt
|
||||||
docs/*.html
|
docs/*.html
|
||||||
docs/generator_flow.png
|
docs/generator_flow.png
|
||||||
example/client
|
examples/simple/simple
|
||||||
example/server
|
examples/network_server/client
|
||||||
example_avr_double/decode_double
|
examples/network_server/server
|
||||||
example_avr_double/encode_double
|
examples/using_double_on_avr/decode_double
|
||||||
example_avr_double/test_conversions
|
examples/using_double_on_avr/encode_double
|
||||||
example_unions/decode
|
examples/using_double_on_avr/test_conversions
|
||||||
example_unions/encode
|
examples/using_union_messages/decode
|
||||||
|
examples/using_union_messages/encode
|
||||||
generator/nanopb_pb2.pyc
|
generator/nanopb_pb2.pyc
|
||||||
|
|||||||
37
CHANGELOG
37
CHANGELOG
@@ -1,4 +1,13 @@
|
|||||||
nanopb-0.2.3
|
nanopb-0.2.4 (2013-11-07)
|
||||||
|
Remove the deprecated NANOPB_INTERNALS functions from public API.
|
||||||
|
Document the security model.
|
||||||
|
Check array and bytes max sizes when encoding (issue 90)
|
||||||
|
Add #defines for maximum encoded message size (issue 89)
|
||||||
|
Add #define tags for extension fields (issue 93)
|
||||||
|
Fix MISRA C violations (issue 91)
|
||||||
|
Clean up pb_field_t definition with typedefs.
|
||||||
|
|
||||||
|
nanopb-0.2.3 (2013-09-18)
|
||||||
Improve compatibility by removing ternary operator from initializations (issue 88)
|
Improve compatibility by removing ternary operator from initializations (issue 88)
|
||||||
Fix build error on Visual C++ (issue 84, patch by Markus Schwarzenberg)
|
Fix build error on Visual C++ (issue 84, patch by Markus Schwarzenberg)
|
||||||
Don't stop on unsupported extension fields (issue 83)
|
Don't stop on unsupported extension fields (issue 83)
|
||||||
@@ -7,7 +16,7 @@ nanopb-0.2.3
|
|||||||
Switch from Makefiles to scons for building the tests
|
Switch from Makefiles to scons for building the tests
|
||||||
Make the tests buildable on Windows
|
Make the tests buildable on Windows
|
||||||
|
|
||||||
nanopb-0.2.2
|
nanopb-0.2.2 (2013-08-18)
|
||||||
Add support for extension fields (issue 17)
|
Add support for extension fields (issue 17)
|
||||||
Fix unknown fields in empty message (issue 78)
|
Fix unknown fields in empty message (issue 78)
|
||||||
Include the field tags in the generated .pb.h file.
|
Include the field tags in the generated .pb.h file.
|
||||||
@@ -16,7 +25,7 @@ nanopb-0.2.2
|
|||||||
Documentation improvements (issues 12, 77 and others)
|
Documentation improvements (issues 12, 77 and others)
|
||||||
Improved tests
|
Improved tests
|
||||||
|
|
||||||
nanopb-0.2.1
|
nanopb-0.2.1 (2013-04-14)
|
||||||
NOTE: The default callback function signature has changed.
|
NOTE: The default callback function signature has changed.
|
||||||
If you don't want to update your code, define PB_OLD_CALLBACK_STYLE.
|
If you don't want to update your code, define PB_OLD_CALLBACK_STYLE.
|
||||||
|
|
||||||
@@ -33,7 +42,7 @@ nanopb-0.2.1
|
|||||||
Various new generator options
|
Various new generator options
|
||||||
Improved tests
|
Improved tests
|
||||||
|
|
||||||
nanopb-0.2.0
|
nanopb-0.2.0 (2013-03-02)
|
||||||
NOTE: This release requires you to regenerate all .pb.c
|
NOTE: This release requires you to regenerate all .pb.c
|
||||||
files. Files generated by older versions will not
|
files. Files generated by older versions will not
|
||||||
compile anymore.
|
compile anymore.
|
||||||
@@ -46,7 +55,7 @@ nanopb-0.2.0
|
|||||||
Add option to give file extension to generator (by Michael Haberler)
|
Add option to give file extension to generator (by Michael Haberler)
|
||||||
Documentation updates
|
Documentation updates
|
||||||
|
|
||||||
nanopb-0.1.9
|
nanopb-0.1.9 (2013-02-13)
|
||||||
Fixed error message bugs (issues 52, 56)
|
Fixed error message bugs (issues 52, 56)
|
||||||
Sanitize #ifndef filename (issue 50)
|
Sanitize #ifndef filename (issue 50)
|
||||||
Performance improvements
|
Performance improvements
|
||||||
@@ -57,13 +66,13 @@ nanopb-0.1.9
|
|||||||
Added generator option to make message structs packed. (issue 49)
|
Added generator option to make message structs packed. (issue 49)
|
||||||
Add more test cases.
|
Add more test cases.
|
||||||
|
|
||||||
nanopb-0.1.8
|
nanopb-0.1.8 (2012-12-13)
|
||||||
Fix bugs in the enum short names introduced in 0.1.7 (issues 42, 43)
|
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 STATIC_ASSERT macro when using multiple .proto files. (issue 41)
|
||||||
Fix missing initialization of istream.errmsg
|
Fix missing initialization of istream.errmsg
|
||||||
Make tests/Makefile work for non-gcc compilers (issue 40)
|
Make tests/Makefile work for non-gcc compilers (issue 40)
|
||||||
|
|
||||||
nanopb-0.1.7
|
nanopb-0.1.7 (2012-11-11)
|
||||||
Remove "skip" mode from pb_istream_t callbacks. Example implementation had a bug. (issue 37)
|
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)
|
Add option to use shorter names for enum values (issue 38)
|
||||||
Improve options support in generator (issues 12, 30)
|
Improve options support in generator (issues 12, 30)
|
||||||
@@ -73,19 +82,19 @@ nanopb-0.1.7
|
|||||||
Add buffer size check in example (issue 34)
|
Add buffer size check in example (issue 34)
|
||||||
Fix build warnings on MS compilers (issue 33)
|
Fix build warnings on MS compilers (issue 33)
|
||||||
|
|
||||||
nanopb-0.1.6
|
nanopb-0.1.6 (2012-09-02)
|
||||||
Reorganize the field decoder interface (issue 2)
|
Reorganize the field decoder interface (issue 2)
|
||||||
Improve performance in submessage decoding (issue 28)
|
Improve performance in submessage decoding (issue 28)
|
||||||
Implement error messages in the decoder side (issue 7)
|
Implement error messages in the decoder side (issue 7)
|
||||||
Extended testcases (alltypes test is now complete).
|
Extended testcases (alltypes test is now complete).
|
||||||
Fix some compiler warnings (issues 25, 26, 27, 32).
|
Fix some compiler warnings (issues 25, 26, 27, 32).
|
||||||
|
|
||||||
nanopb-0.1.5
|
nanopb-0.1.5 (2012-08-04)
|
||||||
Fix bug in decoder with packed arrays (issue 23).
|
Fix bug in decoder with packed arrays (issue 23).
|
||||||
Extended testcases.
|
Extended testcases.
|
||||||
Fix some compiler warnings.
|
Fix some compiler warnings.
|
||||||
|
|
||||||
nanopb-0.1.4
|
nanopb-0.1.4 (2012-07-05)
|
||||||
Add compile-time options for easy-to-use >255 field support.
|
Add compile-time options for easy-to-use >255 field support.
|
||||||
Improve the detection of missing required fields.
|
Improve the detection of missing required fields.
|
||||||
Added example on how to handle union messages.
|
Added example on how to handle union messages.
|
||||||
@@ -93,20 +102,20 @@ nanopb-0.1.4
|
|||||||
Fix problems that stopped the code from compiling with some compilers.
|
Fix problems that stopped the code from compiling with some compilers.
|
||||||
Fix some compiler warnings.
|
Fix some compiler warnings.
|
||||||
|
|
||||||
nanopb-0.1.3
|
nanopb-0.1.3 (2012-06-12)
|
||||||
Refactor the field encoder interface.
|
Refactor the field encoder interface.
|
||||||
Improve generator error messages (issue 5)
|
Improve generator error messages (issue 5)
|
||||||
Add descriptor.proto into the #include exclusion list
|
Add descriptor.proto into the #include exclusion list
|
||||||
Fix some compiler warnings.
|
Fix some compiler warnings.
|
||||||
|
|
||||||
nanopb-0.1.2
|
nanopb-0.1.2 (2012-02-15)
|
||||||
Make the generator to generate include for other .proto files (issue 4).
|
Make the generator to generate include for other .proto files (issue 4).
|
||||||
Fixed generator not working on Windows (issue 3)
|
Fixed generator not working on Windows (issue 3)
|
||||||
|
|
||||||
nanopb-0.1.1
|
nanopb-0.1.1 (2012-01-14)
|
||||||
Fixed bug in encoder with 'bytes' fields (issue 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.
|
Fixed a bug in the generator that caused a compiler error on sfixed32 and sfixed64 fields.
|
||||||
Extended testcases.
|
Extended testcases.
|
||||||
|
|
||||||
nanopb-0.1.0
|
nanopb-0.1.0 (2012-01-06)
|
||||||
First stable release.
|
First stable release.
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ function(NANOPB_GENERATE_CPP SRCS HDRS)
|
|||||||
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
|
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
|
||||||
COMMAND python
|
COMMAND python
|
||||||
ARGS ${NANOPB_GENERATOR_EXECUTABLE} ${FIL_WE}.pb
|
ARGS ${NANOPB_GENERATOR_EXECUTABLE} ${FIL_WE}.pb
|
||||||
DEPENDS ${FIL_WE}.pb
|
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb"
|
||||||
COMMENT "Running nanopb generator on ${FIL_WE}.pb"
|
COMMENT "Running nanopb generator on ${FIL_WE}.pb"
|
||||||
VERBATIM )
|
VERBATIM )
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
all: index.html concepts.html reference.html \
|
all: index.html concepts.html reference.html security.html \
|
||||||
generator_flow.png
|
generator_flow.png
|
||||||
|
|
||||||
%.png: %.svg
|
%.png: %.svg
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
1) `Overview`_
|
1) `Overview`_
|
||||||
2) `Concepts`_
|
2) `Concepts`_
|
||||||
3) `API reference`_
|
3) `API reference`_
|
||||||
|
4) `Security model`_
|
||||||
|
|
||||||
.. _`Overview`: index.html
|
.. _`Overview`: index.html
|
||||||
.. _`Concepts`: concepts.html
|
.. _`Concepts`: concepts.html
|
||||||
.. _`API reference`: reference.html
|
.. _`API reference`: reference.html
|
||||||
|
.. _`Security model`: security.html
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ __BIG_ENDIAN__ Set this if your platform stores integers and
|
|||||||
systems (different layout for ints and floats)
|
systems (different layout for ints and floats)
|
||||||
are currently not supported.
|
are currently not supported.
|
||||||
NANOPB_INTERNALS Set this to expose the field encoder functions
|
NANOPB_INTERNALS Set this to expose the field encoder functions
|
||||||
that are hidden since nanopb-0.1.3.
|
that are hidden since nanopb-0.1.3. Starting
|
||||||
|
with nanopb-0.2.4, this flag does nothing. Use
|
||||||
|
the newer functions that have better interface.
|
||||||
PB_MAX_REQUIRED_FIELDS Maximum number of required fields to check for
|
PB_MAX_REQUIRED_FIELDS Maximum number of required fields to check for
|
||||||
presence. Default value is 64. Increases stack
|
presence. Default value is 64. Increases stack
|
||||||
usage 1 byte per every 8 fields. Compiler
|
usage 1 byte per every 8 fields. Compiler
|
||||||
|
|||||||
79
docs/security.rst
Normal file
79
docs/security.rst
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
======================
|
||||||
|
Nanopb: Security model
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. include :: menu.rst
|
||||||
|
|
||||||
|
.. contents ::
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Importance of security in a Protocol Buffers library
|
||||||
|
====================================================
|
||||||
|
In the context of protocol buffers, security comes into play when decoding
|
||||||
|
untrusted data. Naturally, if the attacker can modify the contents of a
|
||||||
|
protocol buffers message, he can feed the application any values possible.
|
||||||
|
Therefore the application itself must be prepared to receive untrusted values.
|
||||||
|
|
||||||
|
Where nanopb plays a part is preventing the attacker from running arbitrary
|
||||||
|
code on the target system. Mostly this means that there must not be any
|
||||||
|
possibility to cause buffer overruns, memory corruption or invalid pointers
|
||||||
|
by the means of crafting a malicious message.
|
||||||
|
|
||||||
|
Division of trusted and untrusted data
|
||||||
|
======================================
|
||||||
|
The following data is regarded as **trusted**. It must be under the control of
|
||||||
|
the application writer. Malicious data in these structures could cause
|
||||||
|
security issues, such as execution of arbitrary code:
|
||||||
|
|
||||||
|
1. Callback and extension fields in message structures given to pb_encode()
|
||||||
|
and pb_decode(). These fields are memory pointers, and are generated
|
||||||
|
depending on the .proto file.
|
||||||
|
2. The automatically generated field definitions, i.e. *pb_field_t* lists.
|
||||||
|
3. Contents of the *pb_istream_t* and *pb_ostream_t* structures (this does not
|
||||||
|
mean the contents of the stream itself, just the stream definition).
|
||||||
|
|
||||||
|
The following data is regarded as **untrusted**. Invalid/malicious data in
|
||||||
|
these will cause "garbage in, garbage out" behaviour. It will not cause
|
||||||
|
buffer overflows, information disclosure or other security problems:
|
||||||
|
|
||||||
|
1. All data read from *pb_istream_t*.
|
||||||
|
2. All fields in message structures, except callbacks and extensions.
|
||||||
|
(Beginning with nanopb-0.2.4, in earlier versions the field sizes are partially unchecked.)
|
||||||
|
|
||||||
|
Invariants
|
||||||
|
==========
|
||||||
|
The following invariants are maintained during operation, even if the
|
||||||
|
untrusted data has been maliciously crafted:
|
||||||
|
|
||||||
|
1. Nanopb will never read more than *bytes_left* bytes from *pb_istream_t*.
|
||||||
|
2. Nanopb will never write more than *max_size* bytes to *pb_ostream_t*.
|
||||||
|
3. Nanopb will never access memory out of bounds of the message structure.
|
||||||
|
4. After pb_decode() returns successfully, the message structure will be
|
||||||
|
internally consistent:
|
||||||
|
|
||||||
|
- The *count* fields of arrays will not exceed the array size.
|
||||||
|
- The *size* field of bytes will not exceed the allocated size.
|
||||||
|
- All string fields will have null terminator.
|
||||||
|
|
||||||
|
5. After pb_encode() returns successfully, the resulting message is a valid
|
||||||
|
protocol buffers message. (Except if user-defined callbacks write incorrect
|
||||||
|
data.)
|
||||||
|
|
||||||
|
Further considerations
|
||||||
|
======================
|
||||||
|
Even if the nanopb library is free of any security issues, there are still
|
||||||
|
several possible attack vectors that the application author must consider.
|
||||||
|
The following list is not comprehensive:
|
||||||
|
|
||||||
|
1. Stack usage may depend on the contents of the message. The message
|
||||||
|
definition places an upper bound on how much stack will be used. Tests
|
||||||
|
should be run with all fields present, to record the maximum possible
|
||||||
|
stack usage.
|
||||||
|
2. Callbacks can do anything. The code for the callbacks must be carefully
|
||||||
|
checked if they are used with untrusted data.
|
||||||
|
3. If using stream input, a maximum size should be set in *pb_istream_t* to
|
||||||
|
stop a denial of service attack from using an infinite message.
|
||||||
|
4. If using network sockets as streams, a timeout should be set to stop
|
||||||
|
denial of service attacks.
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
|
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
|
||||||
nanopb_version = "nanopb-0.2.3"
|
nanopb_version = "nanopb-0.2.5-dev"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import google.protobuf.descriptor_pb2 as descriptor
|
import google.protobuf.descriptor_pb2 as descriptor
|
||||||
@@ -38,22 +38,22 @@ except:
|
|||||||
import time
|
import time
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
# Values are tuple (c type, pb type)
|
# Values are tuple (c type, pb type, encoded size)
|
||||||
FieldD = descriptor.FieldDescriptorProto
|
FieldD = descriptor.FieldDescriptorProto
|
||||||
datatypes = {
|
datatypes = {
|
||||||
FieldD.TYPE_BOOL: ('bool', 'BOOL'),
|
FieldD.TYPE_BOOL: ('bool', 'BOOL', 1),
|
||||||
FieldD.TYPE_DOUBLE: ('double', 'DOUBLE'),
|
FieldD.TYPE_DOUBLE: ('double', 'DOUBLE', 8),
|
||||||
FieldD.TYPE_FIXED32: ('uint32_t', 'FIXED32'),
|
FieldD.TYPE_FIXED32: ('uint32_t', 'FIXED32', 4),
|
||||||
FieldD.TYPE_FIXED64: ('uint64_t', 'FIXED64'),
|
FieldD.TYPE_FIXED64: ('uint64_t', 'FIXED64', 8),
|
||||||
FieldD.TYPE_FLOAT: ('float', 'FLOAT'),
|
FieldD.TYPE_FLOAT: ('float', 'FLOAT', 4),
|
||||||
FieldD.TYPE_INT32: ('int32_t', 'INT32'),
|
FieldD.TYPE_INT32: ('int32_t', 'INT32', 5),
|
||||||
FieldD.TYPE_INT64: ('int64_t', 'INT64'),
|
FieldD.TYPE_INT64: ('int64_t', 'INT64', 10),
|
||||||
FieldD.TYPE_SFIXED32: ('int32_t', 'SFIXED32'),
|
FieldD.TYPE_SFIXED32: ('int32_t', 'SFIXED32', 4),
|
||||||
FieldD.TYPE_SFIXED64: ('int64_t', 'SFIXED64'),
|
FieldD.TYPE_SFIXED64: ('int64_t', 'SFIXED64', 8),
|
||||||
FieldD.TYPE_SINT32: ('int32_t', 'SINT32'),
|
FieldD.TYPE_SINT32: ('int32_t', 'SINT32', 5),
|
||||||
FieldD.TYPE_SINT64: ('int64_t', 'SINT64'),
|
FieldD.TYPE_SINT64: ('int64_t', 'SINT64', 10),
|
||||||
FieldD.TYPE_UINT32: ('uint32_t', 'UINT32'),
|
FieldD.TYPE_UINT32: ('uint32_t', 'UINT32', 5),
|
||||||
FieldD.TYPE_UINT64: ('uint64_t', 'UINT64')
|
FieldD.TYPE_UINT64: ('uint64_t', 'UINT64', 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Names:
|
class Names:
|
||||||
@@ -83,6 +83,55 @@ def names_from_type_name(type_name):
|
|||||||
raise NotImplementedError("Lookup of non-absolute type names is not supported")
|
raise NotImplementedError("Lookup of non-absolute type names is not supported")
|
||||||
return Names(type_name[1:].split('.'))
|
return Names(type_name[1:].split('.'))
|
||||||
|
|
||||||
|
def varint_max_size(max_value):
|
||||||
|
'''Returns the maximum number of bytes a varint can take when encoded.'''
|
||||||
|
for i in range(1, 11):
|
||||||
|
if (max_value >> (i * 7)) == 0:
|
||||||
|
return i
|
||||||
|
raise ValueError("Value too large for varint: " + str(max_value))
|
||||||
|
|
||||||
|
assert varint_max_size(0) == 1
|
||||||
|
assert varint_max_size(127) == 1
|
||||||
|
assert varint_max_size(128) == 2
|
||||||
|
|
||||||
|
class EncodedSize:
|
||||||
|
'''Class used to represent the encoded size of a field or a message.
|
||||||
|
Consists of a combination of symbolic sizes and integer sizes.'''
|
||||||
|
def __init__(self, value = 0, symbols = []):
|
||||||
|
if isinstance(value, (str, Names)):
|
||||||
|
symbols = [str(value)]
|
||||||
|
value = 0
|
||||||
|
self.value = value
|
||||||
|
self.symbols = symbols
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if isinstance(other, (int, long)):
|
||||||
|
return EncodedSize(self.value + other, self.symbols)
|
||||||
|
elif isinstance(other, (str, Names)):
|
||||||
|
return EncodedSize(self.value, self.symbols + [str(other)])
|
||||||
|
elif isinstance(other, EncodedSize):
|
||||||
|
return EncodedSize(self.value + other.value, self.symbols + other.symbols)
|
||||||
|
else:
|
||||||
|
raise ValueError("Cannot add size: " + repr(other))
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
if isinstance(other, (int, long)):
|
||||||
|
return EncodedSize(self.value * other, [str(other) + '*' + s for s in self.symbols])
|
||||||
|
else:
|
||||||
|
raise ValueError("Cannot multiply size: " + repr(other))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if not self.symbols:
|
||||||
|
return str(self.value)
|
||||||
|
else:
|
||||||
|
return '(' + str(self.value) + ' + ' + ' + '.join(self.symbols) + ')'
|
||||||
|
|
||||||
|
def upperlimit(self):
|
||||||
|
if not self.symbols:
|
||||||
|
return self.value
|
||||||
|
else:
|
||||||
|
return 2**32 - 1
|
||||||
|
|
||||||
class Enum:
|
class Enum:
|
||||||
def __init__(self, names, desc, enum_options):
|
def __init__(self, names, desc, enum_options):
|
||||||
'''desc is EnumDescriptorProto'''
|
'''desc is EnumDescriptorProto'''
|
||||||
@@ -113,6 +162,7 @@ class Field:
|
|||||||
self.max_size = None
|
self.max_size = None
|
||||||
self.max_count = None
|
self.max_count = None
|
||||||
self.array_decl = ""
|
self.array_decl = ""
|
||||||
|
self.enc_size = None
|
||||||
|
|
||||||
# Parse field options
|
# Parse field options
|
||||||
if field_options.HasField("max_size"):
|
if field_options.HasField("max_size"):
|
||||||
@@ -141,12 +191,13 @@ class Field:
|
|||||||
|
|
||||||
# Decide the C data type to use in the struct.
|
# Decide the C data type to use in the struct.
|
||||||
if datatypes.has_key(desc.type):
|
if datatypes.has_key(desc.type):
|
||||||
self.ctype, self.pbtype = datatypes[desc.type]
|
self.ctype, self.pbtype, self.enc_size = datatypes[desc.type]
|
||||||
elif desc.type == FieldD.TYPE_ENUM:
|
elif desc.type == FieldD.TYPE_ENUM:
|
||||||
self.pbtype = 'ENUM'
|
self.pbtype = 'ENUM'
|
||||||
self.ctype = names_from_type_name(desc.type_name)
|
self.ctype = names_from_type_name(desc.type_name)
|
||||||
if self.default is not None:
|
if self.default is not None:
|
||||||
self.default = self.ctype + self.default
|
self.default = self.ctype + self.default
|
||||||
|
self.enc_size = 5 # protoc rejects enum values > 32 bits
|
||||||
elif desc.type == FieldD.TYPE_STRING:
|
elif desc.type == FieldD.TYPE_STRING:
|
||||||
self.pbtype = 'STRING'
|
self.pbtype = 'STRING'
|
||||||
if self.max_size is None:
|
if self.max_size is None:
|
||||||
@@ -154,15 +205,18 @@ class Field:
|
|||||||
else:
|
else:
|
||||||
self.ctype = 'char'
|
self.ctype = 'char'
|
||||||
self.array_decl += '[%d]' % self.max_size
|
self.array_decl += '[%d]' % self.max_size
|
||||||
|
self.enc_size = varint_max_size(self.max_size) + self.max_size
|
||||||
elif desc.type == FieldD.TYPE_BYTES:
|
elif desc.type == FieldD.TYPE_BYTES:
|
||||||
self.pbtype = 'BYTES'
|
self.pbtype = 'BYTES'
|
||||||
if self.max_size is None:
|
if self.max_size is None:
|
||||||
can_be_static = False
|
can_be_static = False
|
||||||
else:
|
else:
|
||||||
self.ctype = self.struct_name + self.name + 't'
|
self.ctype = self.struct_name + self.name + 't'
|
||||||
|
self.enc_size = varint_max_size(self.max_size) + self.max_size
|
||||||
elif desc.type == FieldD.TYPE_MESSAGE:
|
elif desc.type == FieldD.TYPE_MESSAGE:
|
||||||
self.pbtype = 'MESSAGE'
|
self.pbtype = 'MESSAGE'
|
||||||
self.ctype = self.submsgname = names_from_type_name(desc.type_name)
|
self.ctype = self.submsgname = names_from_type_name(desc.type_name)
|
||||||
|
self.enc_size = None # Needs to be filled in after the message type is available
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(desc.type)
|
raise NotImplementedError(desc.type)
|
||||||
|
|
||||||
@@ -277,6 +331,51 @@ class Field:
|
|||||||
|
|
||||||
return max(self.tag, self.max_size, self.max_count)
|
return max(self.tag, self.max_size, self.max_count)
|
||||||
|
|
||||||
|
def encoded_size(self, allmsgs):
|
||||||
|
'''Return the maximum size that this field can take when encoded,
|
||||||
|
including the field tag. If the size cannot be determined, returns
|
||||||
|
None.'''
|
||||||
|
|
||||||
|
if self.allocation != 'STATIC':
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.pbtype == 'MESSAGE':
|
||||||
|
for msg in allmsgs:
|
||||||
|
if msg.name == self.submsgname:
|
||||||
|
encsize = msg.encoded_size(allmsgs)
|
||||||
|
if encsize is None:
|
||||||
|
return None # Submessage size is indeterminate
|
||||||
|
|
||||||
|
# Include submessage length prefix
|
||||||
|
encsize += varint_max_size(encsize.upperlimit())
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Submessage cannot be found, this currently occurs when
|
||||||
|
# the submessage type is defined in a different file.
|
||||||
|
# Instead of direct numeric value, reference the size that
|
||||||
|
# has been #defined in the other file.
|
||||||
|
encsize = EncodedSize(self.submsgname + 'size')
|
||||||
|
|
||||||
|
# We will have to make a conservative assumption on the length
|
||||||
|
# prefix size, though.
|
||||||
|
encsize += 5
|
||||||
|
|
||||||
|
elif self.enc_size is None:
|
||||||
|
raise RuntimeError("Could not determine encoded size for %s.%s"
|
||||||
|
% (self.struct_name, self.name))
|
||||||
|
else:
|
||||||
|
encsize = EncodedSize(self.enc_size)
|
||||||
|
|
||||||
|
encsize += varint_max_size(self.tag << 3) # Tag + wire type
|
||||||
|
|
||||||
|
if self.rules == 'REPEATED':
|
||||||
|
# Decoders must be always able to handle unpacked arrays.
|
||||||
|
# Therefore we have to reserve space for it, even though
|
||||||
|
# we emit packed arrays ourselves.
|
||||||
|
encsize *= self.max_count
|
||||||
|
|
||||||
|
return encsize
|
||||||
|
|
||||||
|
|
||||||
class ExtensionRange(Field):
|
class ExtensionRange(Field):
|
||||||
def __init__(self, struct_name, range_start, field_options):
|
def __init__(self, struct_name, range_start, field_options):
|
||||||
@@ -305,6 +404,12 @@ class ExtensionRange(Field):
|
|||||||
|
|
||||||
def tags(self):
|
def tags(self):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
def encoded_size(self, allmsgs):
|
||||||
|
# We exclude extensions from the count, because they cannot be known
|
||||||
|
# until runtime. Other option would be to return None here, but this
|
||||||
|
# way the value remains useful if extensions are not used.
|
||||||
|
return EncodedSize(0)
|
||||||
|
|
||||||
class ExtensionField(Field):
|
class ExtensionField(Field):
|
||||||
def __init__(self, struct_name, desc, field_options):
|
def __init__(self, struct_name, desc, field_options):
|
||||||
@@ -318,6 +423,11 @@ class ExtensionField(Field):
|
|||||||
self.skip = False
|
self.skip = False
|
||||||
self.rules = 'OPTEXT'
|
self.rules = 'OPTEXT'
|
||||||
|
|
||||||
|
def tags(self):
|
||||||
|
'''Return the #define for the tag number of this field.'''
|
||||||
|
identifier = '%s_tag' % self.fullname
|
||||||
|
return '#define %-40s %d\n' % (identifier, self.tag)
|
||||||
|
|
||||||
def extension_decl(self):
|
def extension_decl(self):
|
||||||
'''Declaration of the extension type in the .pb.h file'''
|
'''Declaration of the extension type in the .pb.h file'''
|
||||||
if self.skip:
|
if self.skip:
|
||||||
@@ -429,6 +539,18 @@ class Message:
|
|||||||
result += ' PB_LAST_FIELD\n};'
|
result += ' PB_LAST_FIELD\n};'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def encoded_size(self, allmsgs):
|
||||||
|
'''Return the maximum size that this message can take when encoded.
|
||||||
|
If the size cannot be determined, returns None.
|
||||||
|
'''
|
||||||
|
size = EncodedSize(0)
|
||||||
|
for field in self.fields:
|
||||||
|
fsize = field.encoded_size(allmsgs)
|
||||||
|
if fsize is None:
|
||||||
|
return None
|
||||||
|
size += fsize
|
||||||
|
|
||||||
|
return size
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -592,13 +714,24 @@ def generate_header(dependencies, headername, enums, messages, extensions, optio
|
|||||||
for msg in sort_dependencies(messages):
|
for msg in sort_dependencies(messages):
|
||||||
for field in msg.fields:
|
for field in msg.fields:
|
||||||
yield field.tags()
|
yield field.tags()
|
||||||
|
for extension in extensions:
|
||||||
|
yield extension.tags()
|
||||||
yield '\n'
|
yield '\n'
|
||||||
|
|
||||||
yield '/* Struct field encoding specification for nanopb */\n'
|
yield '/* Struct field encoding specification for nanopb */\n'
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
yield msg.fields_declaration() + '\n'
|
yield msg.fields_declaration() + '\n'
|
||||||
|
yield '\n'
|
||||||
|
|
||||||
yield '\n#ifdef __cplusplus\n'
|
yield '/* Maximum encoded size of messages (where known) */\n'
|
||||||
|
for msg in messages:
|
||||||
|
msize = msg.encoded_size(messages)
|
||||||
|
if msize is not None:
|
||||||
|
identifier = '%s_size' % msg.name
|
||||||
|
yield '#define %-40s %s\n' % (identifier, msize)
|
||||||
|
yield '\n'
|
||||||
|
|
||||||
|
yield '#ifdef __cplusplus\n'
|
||||||
yield '} /* extern "C" */\n'
|
yield '} /* extern "C" */\n'
|
||||||
yield '#endif\n'
|
yield '#endif\n'
|
||||||
|
|
||||||
|
|||||||
43
pb.h
43
pb.h
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
/* Version of the nanopb library. Just in case you want to check it in
|
/* Version of the nanopb library. Just in case you want to check it in
|
||||||
* your own program. */
|
* your own program. */
|
||||||
#define NANOPB_VERSION nanopb-0.2.3
|
#define NANOPB_VERSION nanopb-0.2.5-dev
|
||||||
|
|
||||||
/* Include all the system headers needed by nanopb. You will need the
|
/* Include all the system headers needed by nanopb. You will need the
|
||||||
* definitions of the following:
|
* definitions of the following:
|
||||||
@@ -173,6 +173,20 @@ typedef uint8_t pb_type_t;
|
|||||||
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
|
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
|
||||||
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
|
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
|
||||||
|
|
||||||
|
/* Data type used for storing sizes of struct fields
|
||||||
|
* and array counts.
|
||||||
|
*/
|
||||||
|
#if defined(PB_FIELD_32BIT)
|
||||||
|
typedef uint32_t pb_size_t;
|
||||||
|
typedef int32_t pb_ssize_t;
|
||||||
|
#elif defined(PB_FIELD_16BIT)
|
||||||
|
typedef uint16_t pb_size_t;
|
||||||
|
typedef int16_t pb_ssize_t;
|
||||||
|
#else
|
||||||
|
typedef uint8_t pb_size_t;
|
||||||
|
typedef int8_t pb_ssize_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* This structure is used in auto-generated constants
|
/* This structure is used in auto-generated constants
|
||||||
* to specify struct fields.
|
* to specify struct fields.
|
||||||
* You can change field sizes if you need structures
|
* You can change field sizes if you need structures
|
||||||
@@ -184,29 +198,12 @@ typedef uint8_t pb_type_t;
|
|||||||
PB_PACKED_STRUCT_START
|
PB_PACKED_STRUCT_START
|
||||||
typedef struct _pb_field_t pb_field_t;
|
typedef struct _pb_field_t pb_field_t;
|
||||||
struct _pb_field_t {
|
struct _pb_field_t {
|
||||||
|
pb_size_t tag;
|
||||||
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
|
|
||||||
uint8_t tag;
|
|
||||||
pb_type_t type;
|
pb_type_t type;
|
||||||
uint8_t data_offset; /* Offset of field data, relative to previous field. */
|
pb_size_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 */
|
pb_ssize_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 */
|
pb_size_t data_size; /* Data size in bytes for a single item */
|
||||||
uint8_t array_size; /* Maximum number of entries in array */
|
pb_size_t array_size; /* Maximum number of entries in array */
|
||||||
#elif defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
|
|
||||||
uint16_t tag;
|
|
||||||
pb_type_t type;
|
|
||||||
uint8_t data_offset;
|
|
||||||
int8_t size_offset;
|
|
||||||
uint16_t data_size;
|
|
||||||
uint16_t array_size;
|
|
||||||
#else
|
|
||||||
uint32_t tag;
|
|
||||||
pb_type_t type;
|
|
||||||
uint8_t data_offset;
|
|
||||||
int8_t size_offset;
|
|
||||||
uint32_t data_size;
|
|
||||||
uint32_t array_size;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Field definitions for submessage
|
/* Field definitions for submessage
|
||||||
* OR default value for all other non-array, non-callback types
|
* OR default value for all other non-array, non-callback types
|
||||||
|
|||||||
197
pb_decode.c
197
pb_decode.c
@@ -3,11 +3,13 @@
|
|||||||
* 2011 Petteri Aimonen <jpa@kapsi.fi>
|
* 2011 Petteri Aimonen <jpa@kapsi.fi>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The warn_unused_result attribute appeared first in gcc-3.4.0 */
|
/* Use the GCC warn_unused_result attribute to check that all return values
|
||||||
|
* are propagated correctly. On other compilers and gcc before 3.4.0 just
|
||||||
|
* ignore the annotation.
|
||||||
|
*/
|
||||||
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
|
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
|
||||||
#define checkreturn
|
#define checkreturn
|
||||||
#else
|
#else
|
||||||
/* Verify that we remember to check all return values for proper error propagation */
|
|
||||||
#define checkreturn __attribute__((warn_unused_result))
|
#define checkreturn __attribute__((warn_unused_result))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -15,26 +17,49 @@
|
|||||||
#include "pb.h"
|
#include "pb.h"
|
||||||
#include "pb_decode.h"
|
#include "pb_decode.h"
|
||||||
|
|
||||||
typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn;
|
/**************************************
|
||||||
|
* Declarations internal to this file *
|
||||||
|
**************************************/
|
||||||
|
|
||||||
/* --- Function pointers to field decoders ---
|
/* Iterator for pb_field_t list */
|
||||||
* Order in the array must match pb_action_t LTYPE numbering.
|
typedef struct {
|
||||||
*/
|
const pb_field_t *start; /* Start of the pb_field_t array */
|
||||||
static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
|
const pb_field_t *pos; /* Current position of the iterator */
|
||||||
&pb_dec_varint,
|
unsigned field_index; /* Zero-based index of the field. */
|
||||||
&pb_dec_svarint,
|
unsigned required_field_index; /* Zero-based index that counts only the required fields */
|
||||||
&pb_dec_fixed32,
|
void *dest_struct; /* Pointer to the destination structure to decode to */
|
||||||
&pb_dec_fixed64,
|
void *pData; /* Pointer where to store current field value */
|
||||||
|
void *pSize; /* Pointer where to store the size of current array field */
|
||||||
&pb_dec_bytes,
|
} pb_field_iterator_t;
|
||||||
&pb_dec_string,
|
|
||||||
&pb_dec_submessage,
|
|
||||||
NULL /* extensions */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**************
|
|
||||||
* pb_istream *
|
static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count);
|
||||||
**************/
|
static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
|
||||||
|
static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size);
|
||||||
|
static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct);
|
||||||
|
static bool pb_field_next(pb_field_iterator_t *iter);
|
||||||
|
static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag);
|
||||||
|
static bool checkreturn decode_ltype(pb_istream_t *stream, const pb_field_t *field, void *dest, pb_type_t type);
|
||||||
|
static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter);
|
||||||
|
static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter);
|
||||||
|
static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter);
|
||||||
|
static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);
|
||||||
|
static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iterator_t *iter);
|
||||||
|
static bool checkreturn find_extension_field(pb_field_iterator_t *iter);
|
||||||
|
static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct);
|
||||||
|
static bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||||
|
static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||||
|
static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||||
|
static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||||
|
static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||||
|
static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||||
|
static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||||
|
static bool checkreturn pb_skip_varint(pb_istream_t *stream);
|
||||||
|
static bool checkreturn pb_skip_string(pb_istream_t *stream);
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
* pb_istream_t implementation *
|
||||||
|
*******************************/
|
||||||
|
|
||||||
static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count)
|
static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count)
|
||||||
{
|
{
|
||||||
@@ -84,6 +109,26 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read a single byte from input stream. buf may not be NULL.
|
||||||
|
* This is an optimization for the varint decoding. */
|
||||||
|
static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf)
|
||||||
|
{
|
||||||
|
if (!stream->bytes_left)
|
||||||
|
PB_RETURN_ERROR(stream, "end-of-stream");
|
||||||
|
|
||||||
|
#ifndef PB_BUFFER_ONLY
|
||||||
|
if (!stream->callback(stream, buf, 1))
|
||||||
|
PB_RETURN_ERROR(stream, "io error");
|
||||||
|
#else
|
||||||
|
*buf = *(uint8_t*)stream->state;
|
||||||
|
stream->state = (uint8_t*)stream->state + 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
stream->bytes_left--;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize)
|
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize)
|
||||||
{
|
{
|
||||||
pb_istream_t stream;
|
pb_istream_t stream;
|
||||||
@@ -109,7 +154,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
|
|||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
uint32_t result;
|
uint32_t result;
|
||||||
|
|
||||||
if (!pb_read(stream, &byte, 1))
|
if (!pb_readbyte(stream, &byte))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!(byte & 0x80))
|
if (!(byte & 0x80))
|
||||||
@@ -128,7 +173,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
|
|||||||
if (bitpos >= 32)
|
if (bitpos >= 32)
|
||||||
PB_RETURN_ERROR(stream, "varint overflow");
|
PB_RETURN_ERROR(stream, "varint overflow");
|
||||||
|
|
||||||
if (!pb_read(stream, &byte, 1))
|
if (!pb_readbyte(stream, &byte))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
result |= (uint32_t)(byte & 0x7F) << bitpos;
|
result |= (uint32_t)(byte & 0x7F) << bitpos;
|
||||||
@@ -151,7 +196,7 @@ bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest)
|
|||||||
if (bitpos >= 64)
|
if (bitpos >= 64)
|
||||||
PB_RETURN_ERROR(stream, "varint overflow");
|
PB_RETURN_ERROR(stream, "varint overflow");
|
||||||
|
|
||||||
if (!pb_read(stream, &byte, 1))
|
if (!pb_readbyte(stream, &byte))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
result |= (uint64_t)(byte & 0x7F) << bitpos;
|
result |= (uint64_t)(byte & 0x7F) << bitpos;
|
||||||
@@ -253,41 +298,25 @@ static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire
|
|||||||
/* Decode string length from stream and return a substream with limited length.
|
/* Decode string length from stream and return a substream with limited length.
|
||||||
* Remember to close the substream using pb_close_string_substream().
|
* Remember to close the substream using pb_close_string_substream().
|
||||||
*/
|
*/
|
||||||
bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream)
|
bool checkreturn pb_make_string_substream(pb_istream_t *stream, size_t *remaining_length)
|
||||||
{
|
{
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
if (!pb_decode_varint32(stream, &size))
|
if (!pb_decode_varint32(stream, &size))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*substream = *stream;
|
if (stream->bytes_left < size)
|
||||||
if (substream->bytes_left < size)
|
|
||||||
PB_RETURN_ERROR(stream, "parent stream too short");
|
PB_RETURN_ERROR(stream, "parent stream too short");
|
||||||
|
|
||||||
substream->bytes_left = size;
|
*remaining_length = stream->bytes_left - size;
|
||||||
stream->bytes_left -= size;
|
stream->bytes_left = size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream)
|
void pb_close_string_substream(pb_istream_t *stream, size_t remaining_length)
|
||||||
{
|
{
|
||||||
stream->state = substream->state;
|
stream->bytes_left += remaining_length;
|
||||||
|
|
||||||
#ifndef PB_NO_ERRMSG
|
|
||||||
stream->errmsg = substream->errmsg;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterator for pb_field_t list */
|
|
||||||
typedef struct {
|
|
||||||
const pb_field_t *start; /* Start of the pb_field_t array */
|
|
||||||
const pb_field_t *pos; /* Current position of the iterator */
|
|
||||||
unsigned field_index; /* Zero-based index of the field. */
|
|
||||||
unsigned required_field_index; /* Zero-based index that counts only the required fields */
|
|
||||||
void *dest_struct; /* Pointer to the destination structure to decode to */
|
|
||||||
void *pData; /* Pointer where to store current field value */
|
|
||||||
void *pSize; /* Pointer where to store the size of current array field */
|
|
||||||
} pb_field_iterator_t;
|
|
||||||
|
|
||||||
static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct)
|
static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct)
|
||||||
{
|
{
|
||||||
iter->start = iter->pos = fields;
|
iter->start = iter->pos = fields;
|
||||||
@@ -352,22 +381,37 @@ static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag)
|
|||||||
* Decode a single field *
|
* Decode a single field *
|
||||||
*************************/
|
*************************/
|
||||||
|
|
||||||
|
/* Invoke a decoder function based on the ltype of a field. */
|
||||||
|
static bool checkreturn decode_ltype(pb_istream_t *stream,
|
||||||
|
const pb_field_t *field, void *dest, pb_type_t type)
|
||||||
|
{
|
||||||
|
switch (PB_LTYPE(type))
|
||||||
|
{
|
||||||
|
case PB_LTYPE_VARINT: return pb_dec_varint(stream, field, dest);
|
||||||
|
case PB_LTYPE_SVARINT: return pb_dec_svarint(stream, field, dest);
|
||||||
|
case PB_LTYPE_FIXED32: return pb_dec_fixed32(stream, field, dest);
|
||||||
|
case PB_LTYPE_FIXED64: return pb_dec_fixed64(stream, field, dest);
|
||||||
|
case PB_LTYPE_BYTES: return pb_dec_bytes(stream, field, dest);
|
||||||
|
case PB_LTYPE_STRING: return pb_dec_string(stream, field, dest);
|
||||||
|
case PB_LTYPE_SUBMESSAGE: return pb_dec_submessage(stream, field, dest);
|
||||||
|
|
||||||
|
default: PB_RETURN_ERROR(stream, "invalid field type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
|
static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
|
||||||
{
|
{
|
||||||
pb_type_t type;
|
pb_type_t type;
|
||||||
pb_decoder_t func;
|
|
||||||
|
|
||||||
type = iter->pos->type;
|
type = iter->pos->type;
|
||||||
func = PB_DECODERS[PB_LTYPE(type)];
|
|
||||||
|
|
||||||
switch (PB_HTYPE(type))
|
switch (PB_HTYPE(type))
|
||||||
{
|
{
|
||||||
case PB_HTYPE_REQUIRED:
|
case PB_HTYPE_REQUIRED:
|
||||||
return func(stream, iter->pos, iter->pData);
|
return decode_ltype(stream, iter->pos, iter->pData, type);
|
||||||
|
|
||||||
case PB_HTYPE_OPTIONAL:
|
case PB_HTYPE_OPTIONAL:
|
||||||
*(bool*)iter->pSize = true;
|
*(bool*)iter->pSize = true;
|
||||||
return func(stream, iter->pos, iter->pData);
|
return decode_ltype(stream, iter->pos, iter->pData, type);
|
||||||
|
|
||||||
case PB_HTYPE_REPEATED:
|
case PB_HTYPE_REPEATED:
|
||||||
if (wire_type == PB_WT_STRING
|
if (wire_type == PB_WT_STRING
|
||||||
@@ -376,24 +420,25 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
|
|||||||
/* Packed array */
|
/* Packed array */
|
||||||
bool status = true;
|
bool status = true;
|
||||||
size_t *size = (size_t*)iter->pSize;
|
size_t *size = (size_t*)iter->pSize;
|
||||||
pb_istream_t substream;
|
size_t remaining_length;
|
||||||
if (!pb_make_string_substream(stream, &substream))
|
if (!pb_make_string_substream(stream, &remaining_length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
while (substream.bytes_left && *size < iter->pos->array_size)
|
while (stream->bytes_left)
|
||||||
{
|
{
|
||||||
void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size);
|
void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size);
|
||||||
if (!func(&substream, iter->pos, pItem))
|
|
||||||
|
if (*size >= iter->pos->array_size)
|
||||||
|
PB_RETURN_ERROR(stream, "array overflow");
|
||||||
|
|
||||||
|
if (!decode_ltype(stream, iter->pos, pItem, type))
|
||||||
{
|
{
|
||||||
status = false;
|
status = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(*size)++;
|
(*size)++;
|
||||||
}
|
}
|
||||||
pb_close_string_substream(stream, &substream);
|
pb_close_string_substream(stream, remaining_length);
|
||||||
|
|
||||||
if (substream.bytes_left != 0)
|
|
||||||
PB_RETURN_ERROR(stream, "array overflow");
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -406,7 +451,7 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
|
|||||||
PB_RETURN_ERROR(stream, "array overflow");
|
PB_RETURN_ERROR(stream, "array overflow");
|
||||||
|
|
||||||
(*size)++;
|
(*size)++;
|
||||||
return func(stream, iter->pos, pItem);
|
return decode_ltype(stream, iter->pos, pItem, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -429,18 +474,18 @@ static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type
|
|||||||
|
|
||||||
if (wire_type == PB_WT_STRING)
|
if (wire_type == PB_WT_STRING)
|
||||||
{
|
{
|
||||||
pb_istream_t substream;
|
size_t remaining_length;
|
||||||
|
|
||||||
if (!pb_make_string_substream(stream, &substream))
|
if (!pb_make_string_substream(stream, &remaining_length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (!pCallback->funcs.decode(&substream, iter->pos, arg))
|
if (!pCallback->funcs.decode(stream, iter->pos, arg))
|
||||||
PB_RETURN_ERROR(stream, "callback failed");
|
PB_RETURN_ERROR(stream, "callback failed");
|
||||||
} while (substream.bytes_left);
|
} while (stream->bytes_left);
|
||||||
|
|
||||||
pb_close_string_substream(stream, &substream);
|
pb_close_string_substream(stream, remaining_length);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -478,7 +523,7 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
|
|||||||
|
|
||||||
/* Default handler for extension fields. Expects a pb_field_t structure
|
/* Default handler for extension fields. Expects a pb_field_t structure
|
||||||
* in extension->type->arg. */
|
* in extension->type->arg. */
|
||||||
static bool checkreturn default_extension_handler(pb_istream_t *stream,
|
static bool checkreturn default_extension_decoder(pb_istream_t *stream,
|
||||||
pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type)
|
pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type)
|
||||||
{
|
{
|
||||||
const pb_field_t *field = (const pb_field_t*)extension->type->arg;
|
const pb_field_t *field = (const pb_field_t*)extension->type->arg;
|
||||||
@@ -513,7 +558,7 @@ static bool checkreturn decode_extension(pb_istream_t *stream,
|
|||||||
if (extension->type->decode)
|
if (extension->type->decode)
|
||||||
status = extension->type->decode(stream, extension, tag, wire_type);
|
status = extension->type->decode(stream, extension, tag, wire_type);
|
||||||
else
|
else
|
||||||
status = default_extension_handler(stream, extension, tag, wire_type);
|
status = default_extension_decoder(stream, extension, tag, wire_type);
|
||||||
|
|
||||||
if (!status)
|
if (!status)
|
||||||
return false;
|
return false;
|
||||||
@@ -697,14 +742,14 @@ bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void
|
|||||||
|
|
||||||
bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
|
bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
|
||||||
{
|
{
|
||||||
pb_istream_t substream;
|
size_t remaining_length;
|
||||||
bool status;
|
bool status;
|
||||||
|
|
||||||
if (!pb_make_string_substream(stream, &substream))
|
if (!pb_make_string_substream(stream, &remaining_length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
status = pb_decode(&substream, fields, dest_struct);
|
status = pb_decode(stream, fields, dest_struct);
|
||||||
pb_close_string_substream(stream, &substream);
|
pb_close_string_substream(stream, remaining_length);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,10 +892,10 @@ bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, vo
|
|||||||
bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
|
bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
|
||||||
{
|
{
|
||||||
bool status;
|
bool status;
|
||||||
pb_istream_t substream;
|
size_t remaining_length;
|
||||||
const pb_field_t* submsg_fields = (const pb_field_t*)field->ptr;
|
const pb_field_t* submsg_fields = (const pb_field_t*)field->ptr;
|
||||||
|
|
||||||
if (!pb_make_string_substream(stream, &substream))
|
if (!pb_make_string_substream(stream, &remaining_length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (field->ptr == NULL)
|
if (field->ptr == NULL)
|
||||||
@@ -859,10 +904,10 @@ bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field
|
|||||||
/* New array entries need to be initialized, while required and optional
|
/* New array entries need to be initialized, while required and optional
|
||||||
* submessages have already been initialized in the top-level pb_decode. */
|
* submessages have already been initialized in the top-level pb_decode. */
|
||||||
if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
|
if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
|
||||||
status = pb_decode(&substream, submsg_fields, dest);
|
status = pb_decode(stream, submsg_fields, dest);
|
||||||
else
|
else
|
||||||
status = pb_decode_noinit(&substream, submsg_fields, dest);
|
status = pb_decode_noinit(stream, submsg_fields, dest);
|
||||||
|
|
||||||
pb_close_string_substream(stream, &substream);
|
pb_close_string_substream(stream, remaining_length);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
23
pb_decode.h
23
pb_decode.h
@@ -128,27 +128,8 @@ bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
|
|||||||
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
|
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
|
||||||
|
|
||||||
/* Make a limited-length substream for reading a PB_WT_STRING field. */
|
/* 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);
|
bool pb_make_string_substream(pb_istream_t *stream, size_t *remaining_length);
|
||||||
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
|
void pb_close_string_substream(pb_istream_t *stream, size_t remaining_length);
|
||||||
|
|
||||||
|
|
||||||
/*******************************
|
|
||||||
* Internal / legacy functions *
|
|
||||||
*******************************/
|
|
||||||
|
|
||||||
#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);
|
|
||||||
bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
|
||||||
|
|
||||||
bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
|
||||||
bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
|
||||||
bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
|
||||||
|
|
||||||
bool pb_skip_varint(pb_istream_t *stream);
|
|
||||||
bool pb_skip_string(pb_istream_t *stream);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
104
pb_encode.c
104
pb_encode.c
@@ -7,32 +7,36 @@
|
|||||||
#include "pb.h"
|
#include "pb.h"
|
||||||
#include "pb_encode.h"
|
#include "pb_encode.h"
|
||||||
|
|
||||||
/* The warn_unused_result attribute appeared first in gcc-3.4.0 */
|
/* Use the GCC warn_unused_result attribute to check that all return values
|
||||||
|
* are propagated correctly. On other compilers and gcc before 3.4.0 just
|
||||||
|
* ignore the annotation.
|
||||||
|
*/
|
||||||
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
|
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
|
||||||
#define checkreturn
|
#define checkreturn
|
||||||
#else
|
#else
|
||||||
/* Verify that we remember to check all return values for proper error propagation */
|
|
||||||
#define checkreturn __attribute__((warn_unused_result))
|
#define checkreturn __attribute__((warn_unused_result))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
|
/**************************************
|
||||||
|
* Declarations internal to this file *
|
||||||
|
**************************************/
|
||||||
|
static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
||||||
|
static bool checkreturn encode_ltype(pb_ostream_t *stream, const pb_field_t *field, const void *src, pb_type_t type);
|
||||||
|
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count);
|
||||||
|
static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
|
||||||
|
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
|
||||||
|
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
|
||||||
|
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_submessage(pb_ostream_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.
|
* pb_ostream_t implementation *
|
||||||
*/
|
*******************************/
|
||||||
static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
|
|
||||||
&pb_enc_varint,
|
|
||||||
&pb_enc_svarint,
|
|
||||||
&pb_enc_fixed32,
|
|
||||||
&pb_enc_fixed64,
|
|
||||||
|
|
||||||
&pb_enc_bytes,
|
|
||||||
&pb_enc_string,
|
|
||||||
&pb_enc_submessage,
|
|
||||||
NULL /* extensions */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* pb_ostream_t implementation */
|
|
||||||
|
|
||||||
static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
||||||
{
|
{
|
||||||
@@ -49,7 +53,7 @@ pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize)
|
|||||||
{
|
{
|
||||||
pb_ostream_t stream;
|
pb_ostream_t stream;
|
||||||
#ifdef PB_BUFFER_ONLY
|
#ifdef PB_BUFFER_ONLY
|
||||||
stream.callback = (void*)1; /* Just some marker value */
|
stream.callback = (void*)1; /* Just a marker value */
|
||||||
#else
|
#else
|
||||||
stream.callback = &buf_write;
|
stream.callback = &buf_write;
|
||||||
#endif
|
#endif
|
||||||
@@ -82,11 +86,32 @@ bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main encoding stuff */
|
/*************************
|
||||||
|
* Encode a single field *
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
/* Invoke an encoder function based on the ltype of a field. */
|
||||||
|
static bool checkreturn encode_ltype(pb_ostream_t *stream,
|
||||||
|
const pb_field_t *field, const void *src, pb_type_t type)
|
||||||
|
{
|
||||||
|
switch (PB_LTYPE(type))
|
||||||
|
{
|
||||||
|
case PB_LTYPE_VARINT: return pb_enc_varint(stream, field, src);
|
||||||
|
case PB_LTYPE_SVARINT: return pb_enc_svarint(stream, field, src);
|
||||||
|
case PB_LTYPE_FIXED32: return pb_enc_fixed32(stream, field, src);
|
||||||
|
case PB_LTYPE_FIXED64: return pb_enc_fixed64(stream, field, src);
|
||||||
|
case PB_LTYPE_BYTES: return pb_enc_bytes(stream, field, src);
|
||||||
|
case PB_LTYPE_STRING: return pb_enc_string(stream, field, src);
|
||||||
|
case PB_LTYPE_SUBMESSAGE: return pb_enc_submessage(stream, field, src);
|
||||||
|
|
||||||
|
default: PB_RETURN_ERROR(stream, "invalid field type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Encode a static array. Handles the size calculations and possible packing. */
|
/* Encode a static array. Handles the size calculations and possible packing. */
|
||||||
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
|
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
|
||||||
const void *pData, size_t count, pb_encoder_t func)
|
const void *pData, size_t count)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
const void *p;
|
const void *p;
|
||||||
@@ -94,6 +119,9 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
|
|||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (count > field->array_size)
|
||||||
|
PB_RETURN_ERROR(stream, "array max size exceeded");
|
||||||
|
|
||||||
/* We always pack arrays if the datatype allows it. */
|
/* We always pack arrays if the datatype allows it. */
|
||||||
if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
|
if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
|
||||||
@@ -116,7 +144,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
|
|||||||
p = pData;
|
p = pData;
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
if (!func(&sizestream, field, p))
|
if (!encode_ltype(&sizestream, field, p, field->type))
|
||||||
return false;
|
return false;
|
||||||
p = (const char*)p + field->data_size;
|
p = (const char*)p + field->data_size;
|
||||||
}
|
}
|
||||||
@@ -133,7 +161,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
|
|||||||
p = pData;
|
p = pData;
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
if (!func(stream, field, p))
|
if (!encode_ltype(stream, field, p, field->type))
|
||||||
return false;
|
return false;
|
||||||
p = (const char*)p + field->data_size;
|
p = (const char*)p + field->data_size;
|
||||||
}
|
}
|
||||||
@@ -145,7 +173,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
|
|||||||
{
|
{
|
||||||
if (!pb_encode_tag_for_field(stream, field))
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
return false;
|
return false;
|
||||||
if (!func(stream, field, p))
|
if (!encode_ltype(stream, field, p, field->type))
|
||||||
return false;
|
return false;
|
||||||
p = (const char*)p + field->data_size;
|
p = (const char*)p + field->data_size;
|
||||||
}
|
}
|
||||||
@@ -159,12 +187,9 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
|
|||||||
static bool checkreturn encode_static_field(pb_ostream_t *stream,
|
static bool checkreturn encode_static_field(pb_ostream_t *stream,
|
||||||
const pb_field_t *field, const void *pData)
|
const pb_field_t *field, const void *pData)
|
||||||
{
|
{
|
||||||
pb_encoder_t func;
|
|
||||||
const void *pSize;
|
const void *pSize;
|
||||||
bool dummy = true;
|
bool dummy = true;
|
||||||
|
|
||||||
func = PB_ENCODERS[PB_LTYPE(field->type)];
|
|
||||||
|
|
||||||
if (field->size_offset)
|
if (field->size_offset)
|
||||||
pSize = (const char*)pData + field->size_offset;
|
pSize = (const char*)pData + field->size_offset;
|
||||||
else
|
else
|
||||||
@@ -175,7 +200,7 @@ static bool checkreturn encode_static_field(pb_ostream_t *stream,
|
|||||||
case PB_HTYPE_REQUIRED:
|
case PB_HTYPE_REQUIRED:
|
||||||
if (!pb_encode_tag_for_field(stream, field))
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
return false;
|
return false;
|
||||||
if (!func(stream, field, pData))
|
if (!encode_ltype(stream, field, pData, field->type))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -185,13 +210,13 @@ static bool checkreturn encode_static_field(pb_ostream_t *stream,
|
|||||||
if (!pb_encode_tag_for_field(stream, field))
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!func(stream, field, pData))
|
if (!encode_ltype(stream, field, pData, field->type))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PB_HTYPE_REPEATED:
|
case PB_HTYPE_REPEATED:
|
||||||
if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
|
if (!encode_array(stream, field, pData, *(const size_t*)pSize))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -242,7 +267,7 @@ static bool checkreturn encode_field(pb_ostream_t *stream,
|
|||||||
|
|
||||||
/* Default handler for extension fields. Expects to have a pb_field_t
|
/* Default handler for extension fields. Expects to have a pb_field_t
|
||||||
* pointer in the extension->type->arg field. */
|
* pointer in the extension->type->arg field. */
|
||||||
static bool checkreturn default_extension_handler(pb_ostream_t *stream,
|
static bool checkreturn default_extension_encoder(pb_ostream_t *stream,
|
||||||
const pb_extension_t *extension)
|
const pb_extension_t *extension)
|
||||||
{
|
{
|
||||||
const pb_field_t *field = (const pb_field_t*)extension->type->arg;
|
const pb_field_t *field = (const pb_field_t*)extension->type->arg;
|
||||||
@@ -263,7 +288,7 @@ static bool checkreturn encode_extension_field(pb_ostream_t *stream,
|
|||||||
if (extension->type->encode)
|
if (extension->type->encode)
|
||||||
status = extension->type->encode(stream, extension);
|
status = extension->type->encode(stream, extension);
|
||||||
else
|
else
|
||||||
status = default_extension_handler(stream, extension);
|
status = default_extension_encoder(stream, extension);
|
||||||
|
|
||||||
if (!status)
|
if (!status)
|
||||||
return false;
|
return false;
|
||||||
@@ -274,6 +299,10 @@ static bool checkreturn encode_extension_field(pb_ostream_t *stream,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* Encode all fields *
|
||||||
|
*********************/
|
||||||
|
|
||||||
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
|
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
|
||||||
{
|
{
|
||||||
const pb_field_t *field = fields;
|
const pb_field_t *field = fields;
|
||||||
@@ -316,7 +345,9 @@ bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const
|
|||||||
return pb_encode_submessage(stream, fields, src_struct);
|
return pb_encode_submessage(stream, fields, src_struct);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper functions */
|
/********************
|
||||||
|
* Helper functions *
|
||||||
|
********************/
|
||||||
bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
|
bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
|
||||||
{
|
{
|
||||||
uint8_t buffer[10];
|
uint8_t buffer[10];
|
||||||
@@ -518,7 +549,10 @@ bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, c
|
|||||||
bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *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;
|
const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src;
|
||||||
UNUSED(field);
|
|
||||||
|
if (bytes->size + offsetof(pb_bytes_array_t, bytes) > field->data_size)
|
||||||
|
PB_RETURN_ERROR(stream, "bytes size exceeded");
|
||||||
|
|
||||||
return pb_encode_string(stream, bytes->bytes, bytes->size);
|
return pb_encode_string(stream, bytes->bytes, bytes->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
pb_encode.h
18
pb_encode.h
@@ -143,24 +143,6 @@ bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
|
|||||||
*/
|
*/
|
||||||
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
|
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
|
||||||
|
|
||||||
|
|
||||||
/*******************************
|
|
||||||
* Internal / legacy functions *
|
|
||||||
*******************************/
|
|
||||||
|
|
||||||
#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
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ if 'CXXFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CXXFLAGS'])
|
|||||||
add_nanopb_builders(env)
|
add_nanopb_builders(env)
|
||||||
|
|
||||||
# Path to the files shared by tests, and to the nanopb core.
|
# Path to the files shared by tests, and to the nanopb core.
|
||||||
env.Append(CPPPATH = ["#../", "#common"])
|
env.Append(CPPPATH = ["#../", "$COMMON"])
|
||||||
|
|
||||||
# Path for finding nanopb.proto
|
# Path for finding nanopb.proto
|
||||||
env.Append(PROTOCPATH = '#../generator')
|
env.Append(PROTOCPATH = '#../generator')
|
||||||
@@ -78,6 +78,8 @@ if 'gcc' in env['CC']:
|
|||||||
|
|
||||||
# More strict checks on the nanopb core
|
# More strict checks on the nanopb core
|
||||||
env.Append(CORECFLAGS = '-Wextra -Wcast-qual -Wlogical-op -Wconversion')
|
env.Append(CORECFLAGS = '-Wextra -Wcast-qual -Wlogical-op -Wconversion')
|
||||||
|
env.Append(CORECFLAGS = ' -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls')
|
||||||
|
env.Append(CORECFLAGS = ' -Wstack-protector')
|
||||||
elif 'clang' in env['CC']:
|
elif 'clang' in env['CC']:
|
||||||
# CLang
|
# CLang
|
||||||
env.Append(CFLAGS = '-ansi -g -O0 -Wall -Werror')
|
env.Append(CFLAGS = '-ansi -g -O0 -Wall -Werror')
|
||||||
@@ -108,5 +110,10 @@ elif 'cl' in env['CXX']:
|
|||||||
env.Append(CXXFLAGS = '/Zi /W2 /WX')
|
env.Append(CXXFLAGS = '/Zi /W2 /WX')
|
||||||
|
|
||||||
# Now include the SConscript files from all subdirectories
|
# Now include the SConscript files from all subdirectories
|
||||||
SConscript(Glob('*/SConscript'), exports = 'env')
|
import os.path
|
||||||
|
env['VARIANT_DIR'] = 'build'
|
||||||
|
env['BUILD'] = '#' + env['VARIANT_DIR']
|
||||||
|
env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common'
|
||||||
|
for subdir in Glob('*/SConscript'):
|
||||||
|
SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir)))
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
env.NanopbProto("alltypes")
|
env.NanopbProto(["alltypes", "alltypes.options"])
|
||||||
enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "#common/pb_encode.o"])
|
enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o"])
|
||||||
dec = env.Program(["decode_alltypes.c", "alltypes.pb.c", "#common/pb_decode.o"])
|
dec = env.Program(["decode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_decode.o"])
|
||||||
|
|
||||||
env.RunTest(enc)
|
env.RunTest(enc)
|
||||||
env.RunTest([dec, "encode_alltypes.output"])
|
env.RunTest([dec, "encode_alltypes.output"])
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ int main(int argc, char **argv)
|
|||||||
alltypes.end = 1099;
|
alltypes.end = 1099;
|
||||||
|
|
||||||
{
|
{
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[AllTypes_size];
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
/* Now encode it and check if we succeeded. */
|
/* Now encode it and check if we succeeded. */
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
enc = env.Program(["encode_legacy.c", "alltypes_legacy.c", "#common/pb_encode.o"])
|
enc = env.Program(["encode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_encode.o"])
|
||||||
dec = env.Program(["decode_legacy.c", "alltypes_legacy.c", "#common/pb_decode.o"])
|
dec = env.Program(["decode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_decode.o"])
|
||||||
|
|
||||||
env.RunTest(enc)
|
env.RunTest(enc)
|
||||||
env.RunTest([dec, "encode_legacy.output"])
|
env.RunTest([dec, "encode_legacy.output"])
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
enc = env.Program(["encode_buffer.c", "#common/person.pb.c", "#common/pb_encode.o"])
|
enc = env.Program(["encode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o"])
|
||||||
dec = env.Program(["decode_buffer.c", "#common/person.pb.c", "#common/pb_decode.o"])
|
dec = env.Program(["decode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o"])
|
||||||
|
|
||||||
env.RunTest(enc)
|
env.RunTest(enc)
|
||||||
env.RunTest([dec, "encode_buffer.output"])
|
env.RunTest([dec, "encode_buffer.output"])
|
||||||
env.Decode(["encode_buffer.output", "#common/person.proto"], MESSAGE = "Person")
|
env.Decode(["encode_buffer.output", "$COMMON/person.proto"], MESSAGE = "Person")
|
||||||
env.Compare(["decode_buffer.output", "encode_buffer.decoded"])
|
env.Compare(["decode_buffer.output", "encode_buffer.decoded"])
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ bool print_person(pb_istream_t *stream)
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
uint8_t buffer[512];
|
uint8_t buffer[Person_size];
|
||||||
pb_istream_t stream;
|
pb_istream_t stream;
|
||||||
size_t count;
|
size_t count;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
uint8_t buffer[512];
|
uint8_t buffer[Person_size];
|
||||||
pb_ostream_t stream;
|
pb_ostream_t stream;
|
||||||
|
|
||||||
/* Initialize the structure with constants */
|
/* Initialize the structure with constants */
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
enc = env.Program(["encode_stream.c", "#common/person.pb.c", "#common/pb_encode.o"])
|
enc = env.Program(["encode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o"])
|
||||||
dec = env.Program(["decode_stream.c", "#common/person.pb.c", "#common/pb_decode.o"])
|
dec = env.Program(["decode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o"])
|
||||||
|
|
||||||
env.RunTest(enc)
|
env.RunTest(enc)
|
||||||
env.RunTest([dec, "encode_stream.output"])
|
env.RunTest([dec, "encode_stream.output"])
|
||||||
env.Decode(["encode_stream.output", "#common/person.proto"], MESSAGE = "Person")
|
env.Decode(["encode_stream.output", "$COMMON/person.proto"], MESSAGE = "Person")
|
||||||
env.Compare(["decode_stream.output", "encode_stream.decoded"])
|
env.Compare(["decode_stream.output", "encode_stream.decoded"])
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ Import("env")
|
|||||||
c = Copy("$TARGET", "$SOURCE")
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
env.Command("alltypes.pb.h", "#alltypes/alltypes.pb.h", c)
|
env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
|
||||||
env.Command("alltypes.pb.c", "#alltypes/alltypes.pb.c", c)
|
env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c)
|
||||||
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
|
||||||
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
# Define the compilation options
|
# Define the compilation options
|
||||||
opts = env.Clone()
|
opts = env.Clone()
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
env.NanopbProto("callbacks")
|
env.NanopbProto("callbacks")
|
||||||
enc = env.Program(["encode_callbacks.c", "callbacks.pb.c", "#common/pb_encode.o"])
|
enc = env.Program(["encode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_encode.o"])
|
||||||
dec = env.Program(["decode_callbacks.c", "callbacks.pb.c", "#common/pb_decode.o"])
|
dec = env.Program(["decode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_decode.o"])
|
||||||
|
|
||||||
env.RunTest(enc)
|
env.RunTest(enc)
|
||||||
env.RunTest([dec, "encode_callbacks.output"])
|
env.RunTest([dec, "encode_callbacks.output"])
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ message StringMessage {
|
|||||||
required string data = 1 [(nanopb).max_size = 10];
|
required string data = 1 [(nanopb).max_size = 10];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message BytesMessage {
|
||||||
|
required bytes data = 1 [(nanopb).max_size = 16];
|
||||||
|
}
|
||||||
|
|
||||||
message CallbackArray {
|
message CallbackArray {
|
||||||
// We cheat a bit and use this message for testing other types, too.
|
// We cheat a bit and use this message for testing other types, too.
|
||||||
// Nanopb does not care about the actual defined data type for callback
|
// Nanopb does not care about the actual defined data type for callback
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ Import("env")
|
|||||||
c = Copy("$TARGET", "$SOURCE")
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
env.Command("pb_encode.cxx", "#../pb_encode.c", c)
|
env.Command("pb_encode.cxx", "#../pb_encode.c", c)
|
||||||
env.Command("pb_decode.cxx", "#../pb_decode.c", c)
|
env.Command("pb_decode.cxx", "#../pb_decode.c", c)
|
||||||
env.Command("alltypes.pb.h", "#alltypes/alltypes.pb.h", c)
|
env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
|
||||||
env.Command("alltypes.pb.cxx", "#alltypes/alltypes.pb.c", c)
|
env.Command("alltypes.pb.cxx", "$BUILD/alltypes/alltypes.pb.c", c)
|
||||||
env.Command("encode_alltypes.cxx", "#alltypes/encode_alltypes.c", c)
|
env.Command("encode_alltypes.cxx", "$BUILD/alltypes/encode_alltypes.c", c)
|
||||||
env.Command("decode_alltypes.cxx", "#alltypes/decode_alltypes.c", c)
|
env.Command("decode_alltypes.cxx", "$BUILD/alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
# Now build and run the test normally.
|
# Now build and run the test normally.
|
||||||
enc = env.Program(["encode_alltypes.cxx", "alltypes.pb.cxx", "pb_encode.cxx"])
|
enc = env.Program(["encode_alltypes.cxx", "alltypes.pb.cxx", "pb_encode.cxx"])
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Import('env')
|
Import('env')
|
||||||
p = env.Program(["decode_unittests.c", "#common/unittestproto.pb.c", "#common/pb_decode.o"])
|
p = env.Program(["decode_unittests.c", "$COMMON/unittestproto.pb.c"])
|
||||||
env.RunTest(p)
|
env.RunTest(p)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#define NANOPB_INTERNALS
|
/* This includes the whole .c file to get access to static functions. */
|
||||||
|
#include "pb_decode.c"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "pb_decode.h"
|
|
||||||
#include "unittests.h"
|
#include "unittests.h"
|
||||||
#include "unittestproto.pb.h"
|
#include "unittestproto.pb.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build and run the stand-alone unit tests for the nanopb encoder part.
|
# Build and run the stand-alone unit tests for the nanopb encoder part.
|
||||||
|
|
||||||
Import('env')
|
Import('env')
|
||||||
p = env.Program(["encode_unittests.c", "#common/unittestproto.pb.c", "#common/pb_encode.o"])
|
p = env.Program(["encode_unittests.c", "$COMMON/unittestproto.pb.c"])
|
||||||
env.RunTest(p)
|
env.RunTest(p)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#define NANOPB_INTERNALS
|
/* This includes the whole .c file to get access to static functions. */
|
||||||
|
#include "pb_encode.c"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "pb_encode.h"
|
|
||||||
#include "unittests.h"
|
#include "unittests.h"
|
||||||
#include "unittestproto.pb.h"
|
#include "unittestproto.pb.h"
|
||||||
|
|
||||||
@@ -172,9 +172,9 @@ int main()
|
|||||||
struct { size_t size; uint8_t bytes[5]; } value = {5, {'x', 'y', 'z', 'z', 'y'}};
|
struct { size_t size; uint8_t bytes[5]; } value = {5, {'x', 'y', 'z', 'z', 'y'}};
|
||||||
|
|
||||||
COMMENT("Test pb_enc_bytes")
|
COMMENT("Test pb_enc_bytes")
|
||||||
TEST(WRITES(pb_enc_bytes(&s, NULL, &value), "\x05xyzzy"))
|
TEST(WRITES(pb_enc_bytes(&s, &BytesMessage_fields[0], &value), "\x05xyzzy"))
|
||||||
value.size = 0;
|
value.size = 0;
|
||||||
TEST(WRITES(pb_enc_bytes(&s, NULL, &value), "\x00"))
|
TEST(WRITES(pb_enc_bytes(&s, &BytesMessage_fields[0], &value), "\x00"))
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -223,6 +223,20 @@ int main()
|
|||||||
TEST(!pb_encode(&s, FloatArray_fields, &msg))
|
TEST(!pb_encode(&s, FloatArray_fields, &msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t buffer[50];
|
||||||
|
pb_ostream_t s;
|
||||||
|
FloatArray msg = {1, {99.0f}};
|
||||||
|
|
||||||
|
COMMENT("Test array size limit in pb_encode")
|
||||||
|
|
||||||
|
s = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
TEST((msg.data_count = 10) && pb_encode(&s, FloatArray_fields, &msg))
|
||||||
|
|
||||||
|
s = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
TEST((msg.data_count = 11) && !pb_encode(&s, FloatArray_fields, &msg))
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
uint8_t buffer[10];
|
uint8_t buffer[10];
|
||||||
pb_ostream_t s;
|
pb_ostream_t s;
|
||||||
@@ -244,6 +258,20 @@ int main()
|
|||||||
"\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"))
|
"\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t buffer[32];
|
||||||
|
pb_ostream_t s;
|
||||||
|
BytesMessage msg = {{3, "xyz"}};
|
||||||
|
|
||||||
|
COMMENT("Test pb_encode with bytes message.")
|
||||||
|
TEST(WRITES(pb_encode(&s, BytesMessage_fields, &msg),
|
||||||
|
"\x0A\x03xyz"))
|
||||||
|
|
||||||
|
msg.data.size = 17; /* More than maximum */
|
||||||
|
TEST(!pb_encode(&s, BytesMessage_fields, &msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
uint8_t buffer[20];
|
uint8_t buffer[20];
|
||||||
pb_ostream_t s;
|
pb_ostream_t s;
|
||||||
@@ -280,6 +308,19 @@ int main()
|
|||||||
TEST(!pb_encode(&s, CallbackContainerContainer_fields, &msg2))
|
TEST(!pb_encode(&s, CallbackContainerContainer_fields, &msg2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t buffer[StringMessage_size];
|
||||||
|
pb_ostream_t s;
|
||||||
|
StringMessage msg = {"0123456789"};
|
||||||
|
|
||||||
|
s = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
COMMENT("Test that StringMessage_size is correct")
|
||||||
|
|
||||||
|
TEST(pb_encode(&s, StringMessage_fields, &msg));
|
||||||
|
TEST(s.bytes_written == StringMessage_size);
|
||||||
|
}
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
fprintf(stdout, "\n\nSome tests FAILED!\n");
|
fprintf(stdout, "\n\nSome tests FAILED!\n");
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ Import("env")
|
|||||||
|
|
||||||
# We use the files from the alltypes test case
|
# We use the files from the alltypes test case
|
||||||
incpath = env.Clone()
|
incpath = env.Clone()
|
||||||
incpath.Append(PROTOCPATH = '#alltypes')
|
incpath.Append(PROTOCPATH = '$BUILD/alltypes')
|
||||||
incpath.Append(CPPPATH = '#alltypes')
|
incpath.Append(CPPPATH = '$BUILD/alltypes')
|
||||||
|
|
||||||
incpath.NanopbProto("extensions")
|
incpath.NanopbProto(["extensions", "extensions.options"])
|
||||||
enc = incpath.Program(["encode_extensions.c", "extensions.pb.c", "#alltypes/alltypes.pb$OBJSUFFIX", "#common/pb_encode.o"])
|
enc = incpath.Program(["encode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_encode.o"])
|
||||||
dec = incpath.Program(["decode_extensions.c", "extensions.pb.c", "#alltypes/alltypes.pb$OBJSUFFIX", "#common/pb_decode.o"])
|
dec = incpath.Program(["decode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_decode.o"])
|
||||||
|
|
||||||
env.RunTest(enc)
|
env.RunTest(enc)
|
||||||
env.RunTest([dec, "encode_extensions.output"])
|
env.RunTest([dec, "encode_extensions.output"])
|
||||||
|
|||||||
@@ -46,5 +46,9 @@ int main(int argc, char **argv)
|
|||||||
fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
|
fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
|
||||||
return 1; /* Failure */
|
return 1; /* Failure */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check that the field tags are properly generated */
|
||||||
|
(void)AllTypes_extensionfield1_tag;
|
||||||
|
(void)ExtensionMessage_AllTypes_extensionfield2_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
dec = env.GetBuildPath('#basic_buffer/${PROGPREFIX}decode_buffer${PROGSUFFIX}')
|
dec = env.GetBuildPath('$BUILD/basic_buffer/${PROGPREFIX}decode_buffer${PROGSUFFIX}')
|
||||||
env.RunTest('person_with_extra_field.output', [dec, "person_with_extra_field.pb"])
|
env.RunTest('person_with_extra_field.output', [dec, "person_with_extra_field.pb"])
|
||||||
env.Compare(["person_with_extra_field.output", "person_with_extra_field.expected"])
|
env.Compare(["person_with_extra_field.output", "person_with_extra_field.expected"])
|
||||||
|
|
||||||
dec = env.GetBuildPath('#basic_stream/${PROGPREFIX}decode_stream${PROGSUFFIX}')
|
dec = env.GetBuildPath('$BUILD/basic_stream/${PROGPREFIX}decode_stream${PROGSUFFIX}')
|
||||||
env.RunTest('person_with_extra_field_stream.output', [dec, "person_with_extra_field.pb"])
|
env.RunTest('person_with_extra_field_stream.output', [dec, "person_with_extra_field.pb"])
|
||||||
env.Compare(["person_with_extra_field_stream.output", "person_with_extra_field.expected"])
|
env.Compare(["person_with_extra_field_stream.output", "person_with_extra_field.expected"])
|
||||||
|
|
||||||
dec2 = env.GetBuildPath('#alltypes/${PROGPREFIX}decode_alltypes${PROGSUFFIX}')
|
dec2 = env.GetBuildPath('$BUILD/alltypes/${PROGPREFIX}decode_alltypes${PROGSUFFIX}')
|
||||||
env.RunTest('alltypes_with_extra_fields.output', [dec2, 'alltypes_with_extra_fields.pb'])
|
env.RunTest('alltypes_with_extra_fields.output', [dec2, 'alltypes_with_extra_fields.pb'])
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ Import("env")
|
|||||||
c = Copy("$TARGET", "$SOURCE")
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
|
||||||
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
env.NanopbProto("alltypes")
|
env.NanopbProto(["alltypes", "alltypes.options"])
|
||||||
|
|
||||||
# Define the compilation options
|
# Define the compilation options
|
||||||
opts = env.Clone()
|
opts = env.Clone()
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ Import("env")
|
|||||||
c = Copy("$TARGET", "$SOURCE")
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
|
||||||
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
env.NanopbProto("alltypes")
|
env.NanopbProto(["alltypes", "alltypes.options"])
|
||||||
|
|
||||||
# Define the compilation options
|
# Define the compilation options
|
||||||
opts = env.Clone()
|
opts = env.Clone()
|
||||||
|
|||||||
11
tests/message_sizes/SConscript
Normal file
11
tests/message_sizes/SConscript
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Test the generation of message size #defines
|
||||||
|
|
||||||
|
Import('env')
|
||||||
|
|
||||||
|
incpath = env.Clone()
|
||||||
|
incpath.Append(PROTOCPATH = '#message_sizes')
|
||||||
|
|
||||||
|
incpath.NanopbProto("messages1")
|
||||||
|
incpath.NanopbProto("messages2")
|
||||||
|
|
||||||
|
incpath.Program(['dummy.c', 'messages1.pb.c', 'messages2.pb.c'])
|
||||||
9
tests/message_sizes/dummy.c
Normal file
9
tests/message_sizes/dummy.c
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* Just test that the file can be compiled successfully. */
|
||||||
|
|
||||||
|
#include "messages2.pb.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return xmit_size;
|
||||||
|
}
|
||||||
|
|
||||||
27
tests/message_sizes/messages1.proto
Normal file
27
tests/message_sizes/messages1.proto
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
enum MessageStatus {
|
||||||
|
FAIL = 0;
|
||||||
|
OK = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
message MessageInfo {
|
||||||
|
required fixed32 msg_id = 1;
|
||||||
|
optional fixed32 interface_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageResponseInfo {
|
||||||
|
required fixed64 interface_id = 1;
|
||||||
|
required fixed32 seq = 2;
|
||||||
|
required fixed32 msg_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageHeader {
|
||||||
|
required MessageInfo info = 1;
|
||||||
|
optional MessageResponseInfo response_info = 2;
|
||||||
|
optional MessageResponse response = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageResponse {
|
||||||
|
required MessageStatus status = 1;
|
||||||
|
required fixed32 seq = 2;
|
||||||
|
}
|
||||||
|
|
||||||
8
tests/message_sizes/messages2.proto
Normal file
8
tests/message_sizes/messages2.proto
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'nanopb.proto';
|
||||||
|
import 'messages1.proto';
|
||||||
|
|
||||||
|
message xmit {
|
||||||
|
required MessageHeader header = 1;
|
||||||
|
required bytes data = 2 [(nanopb).max_size = 128];
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
env.NanopbProto("missing_fields")
|
env.NanopbProto("missing_fields")
|
||||||
test = env.Program(["missing_fields.c", "missing_fields.pb.c", "#common/pb_encode.o", "#common/pb_decode.o"])
|
test = env.Program(["missing_fields.c", "missing_fields.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_decode.o"])
|
||||||
env.RunTest(test)
|
env.RunTest(test)
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ Import("env")
|
|||||||
c = Copy("$TARGET", "$SOURCE")
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
env.Command("alltypes.pb.h", "#alltypes/alltypes.pb.h", c)
|
env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
|
||||||
env.Command("alltypes.pb.c", "#alltypes/alltypes.pb.c", c)
|
env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c)
|
||||||
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
|
||||||
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
# Define the compilation options
|
# Define the compilation options
|
||||||
opts = env.Clone()
|
opts = env.Clone()
|
||||||
|
|||||||
Reference in New Issue
Block a user