Compare commits

..

27 Commits

Author SHA1 Message Date
Petteri Aimonen
62bbe46a45 Publishing nanopb-0.1.9.1 2014-09-11 18:58:16 +03:00
Petteri Aimonen
411db5b450 Update changelog 2014-09-11 18:55:42 +03:00
Petteri Aimonen
29479a7cca Protect against size_t overflows in pb_dec_bytes/pb_dec_string.
Possible consequences of bug:
1) Denial of service by causing a crash
   Possible when all of the following apply:
      - Untrusted data is passed to pb_decode()
      - The top-level message contains a static string field as the first field.
   Causes a single write of '0' byte to 1 byte before the message struct.

2) Remote code execution
   Possible when all of the following apply:
      - 64-bit platform
      - The message or a submessage contains a static string field.
      - Decoding directly from a custom pb_istream_t
      - bytes_left on the stream is set to larger than 4 GB
   Causes a write of up to 4 GB of data past the string field.

--

Detailed analysis follows

In the following consideration, I define "platform bitness" as equal to
number of bits in size_t datatype. Therefore most 8-bit platforms are
regarded as 16-bit for the purposes of this discussion.

1. The overflow in pb_dec_string

The overflow happens in this computation:

uint32_t size;
size_t alloc_size;
alloc_size = size + 1;

There are two ways in which the overflow can occur: In the uint32_t
addition, or in the cast to size_t. This depends on the platform
bitness.

On 32- and 64-bit platforms, the size has to be UINT32_MAX for the
overflow to occur. In that case alloc_size will be 0.

On 16-bit platforms, overflow will happen whenever size is more than
UINT16_MAX, and resulting alloc_size is attacker controlled.

For static fields, the alloc_size value is just checked against the
field data size. For pointer fields, the alloc_size value is passed to
malloc(). End result in both cases is the same, the storage is 0 or
just a few bytes in length.

On 16-bit platforms, another overflow occurs in the call to pb_read(),
when passing the original size. An attacker will want the passed value
to be larger than the alloc_size, therefore the only reasonable choice
is to have size = UINT16_MAX and alloc_size = 0. Any larger multiple
will truncate to the same values.

At this point we have read atleast the tag and the string length of the
message, i.e. atleast 3 bytes. The maximum initial value for stream
bytes_left is SIZE_MAX, thus at this point at most SIZE_MAX-3 bytes are
remaining.

On 32-bit and 16-bit platforms this means that the size passed to
pb_read() is always larger than the number of remaining bytes. This
causes pb_read() to fail immediately, before reading any bytes.

On 64-bit platforms, it is possible for the bytes_left value to be set
to a value larger than UINT32_MAX, which is the wraparound point in
size calculation. In this case pb_read() will succeed and write up to 4
GB of attacker controlled data over the RAM that comes after the string
field.

On all platforms, there is an unconditional write of a terminating null
byte. Because the size of size_t typically reflects the size of the
processor address space, a write at UINT16_MAX or UINT32_MAX bytes
after the string field actually wraps back to before the string field.
Consequently, on 32-bit and 16-bit platforms, the bug causes a single
write of '0' byte at one byte before the string field.

If the string field is in the middle of a message, this will just
corrupt other data in the message struct. Because the message contents
is attacker controlled anyway, this is a non-issue. However, if the
string field is the first field in the top-level message, it can
corrupt other data on the stack/heap before it. Typically a single '0'
write at a location not controlled by attacker is enough only for a
denial-of-service attack.

When using pointer fields and malloc(), the attacker controlled
alloc_size will cause a 0-size allocation to happen. By the same logic
as before, on 32-bit and 16-bit platforms this causes a '0' byte write
only. On 64-bit platforms, however, it will again allow up to 4 GB of
malicious data to be written over memory, if the stream length allows
the read.

2. The overflow in pb_dec_bytes

This overflow happens in the PB_BYTES_ARRAY_T_ALLOCSIZE macro:

The computation is done in size_t data type this time. This means that
an overflow is possible only when n is larger than SIZE_MAX -
offsetof(..). The offsetof value in this case is equal to
sizeof(pb_size_t) bytes.

Because the incoming size value is limited to 32 bits, no overflow can
happen here on 64-bit platforms.

The size will be passed to pb_read(). Like before, on 32-bit and 16-bit
platforms the read will always fail before writing anything.

This leaves only the write of bdest->size as exploitable. On statically
allocated fields, the size field will always be allocated, regardless
of alloc_size. In this case, no buffer overflow is possible here, but
user code could possibly use the attacker controlled size value and
read past a buffer.

If the field is allocated through malloc(), this will allow a write of
sizeof(pb_size_t) attacker controlled bytes to past a 0-byte long
buffer. In typical malloc implementations, this will either fit in
unused alignment padding area, or cause a heap corruption and a crash.
Under very exceptional situation it could allow attacker to influence
the behaviour of malloc(), possibly jumping into an attacker-controlled
location and thus leading to remote code execution.
2014-09-08 18:35:50 +03: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
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
26 changed files with 1410 additions and 53 deletions

View File

@@ -1,3 +1,20 @@
nanopb-0.1.9.1
Fix security issue due to size_t overflows. (issue 132)
NOTE: nanopb-0.1.x is and will remain affected by issue 97.
A fix would be too intrusive for a support release.
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)

View File

@@ -22,6 +22,8 @@ PB_FIELD_32BIT Add support for tag numbers > 65535 and fields la
Increases code size 9 bytes per each field. Compiler error will tell if you need this.
PB_NO_ERRMSG Disables the support for error messages; only error information is the true/false return value.
Decreases the code size by a few hundred bytes.
PB_BUFFER_ONLY Disables the support for custom streams. Only supports encoding to memory buffers.
Speeds up execution and decreases code size slightly.
============================ ================================================================================================
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow raising some datatype limits to suit larger messages.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
nanopb_version = "nanopb-0.1.8"
nanopb_version = "nanopb-0.1.9.1"
try:
import google.protobuf.descriptor_pb2 as descriptor
@@ -306,7 +306,14 @@ class Field:
class Message:
def __init__(self, names, desc, message_options):
self.name = names
self.fields = [Field(self.name, f, get_nanopb_suboptions(f, message_options)) for f in desc.field]
self.fields = []
for f in desc.field:
field_options = get_nanopb_suboptions(f, message_options)
if field_options.type != nanopb_pb2.FT_IGNORE:
self.fields.append(Field(self.name, f, field_options))
self.packed = message_options.packed_struct
self.ordered_fields = self.fields[:]
self.ordered_fields.sort()
@@ -317,7 +324,12 @@ class Message:
def __str__(self):
result = 'typedef struct _%s {\n' % self.name
result += '\n'.join([str(f) for f in self.ordered_fields])
result += '\n} %s;' % self.name
result += '\n}'
if self.packed:
result += ' pb_packed'
result += ' %s;' % self.name
return result
def types(self):
@@ -440,6 +452,16 @@ def sort_dependencies(messages):
if msgname in message_by_name:
yield message_by_name[msgname]
def make_identifier(headername):
'''Make #ifndef identifier that contains uppercase A-Z and digits 0-9'''
result = ""
for c in headername.upper():
if c.isalnum():
result += c
else:
result += '_'
return result
def generate_header(dependencies, headername, enums, messages):
'''Generate content for a header file.
Generates strings, which should be concatenated and stored to file.
@@ -448,7 +470,7 @@ def generate_header(dependencies, headername, enums, messages):
yield '/* Automatically generated nanopb header */\n'
yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
symbol = headername.replace('.', '_').upper()
symbol = make_identifier(headername)
yield '#ifndef _PB_%s_\n' % symbol
yield '#define _PB_%s_\n' % symbol
yield '#include <pb.h>\n\n'
@@ -490,6 +512,7 @@ def generate_header(dependencies, headername, enums, messages):
yield ' setting PB_MAX_REQUIRED_FIELDS to %d or more.\n' % largest_count
yield '#endif\n'
# Add checks for numeric limits
worst = 0
worst_field = ''
checks = []
@@ -527,6 +550,20 @@ def generate_header(dependencies, headername, enums, messages):
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 ' */\n'
yield 'STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n'
yield '\n#ifdef __cplusplus\n'
yield '} /* extern "C" */\n'
yield '#endif\n'

View File

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

6
pb.h
View File

@@ -6,7 +6,7 @@
* see pb_encode.h or pb_decode.h
*/
#define NANOPB_VERSION nanopb-0.1.8
#define NANOPB_VERSION nanopb-0.1.9.1
#include <stdint.h>
#include <stddef.h>
@@ -100,8 +100,8 @@ typedef enum {
/* 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). */
* LTYPE should be valid or 0 (it is ignored, but
* sometimes used to speculatively index an array). */
PB_HTYPE_CALLBACK = 0x30,
PB_HTYPE_MASK = 0xF0

View File

@@ -39,16 +39,20 @@ static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count)
{
uint8_t *source = (uint8_t*)stream->state;
stream->state = source + count;
if (buf != NULL)
memcpy(buf, source, count);
{
while (count--)
*buf++ = *source++;
}
stream->state = source + count;
return true;
}
bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
{
#ifndef PB_BUFFER_ONLY
if (buf == NULL && stream->callback != buf_read)
{
/* Skip input bytes */
@@ -63,12 +67,18 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
return pb_read(stream, tmp, count);
}
#endif
if (stream->bytes_left < count)
PB_RETURN_ERROR(stream, "end-of-stream");
#ifndef PB_BUFFER_ONLY
if (!stream->callback(stream, buf, count))
PB_RETURN_ERROR(stream, "io error");
#else
if (!buf_read(stream, buf, count))
return false;
#endif
stream->bytes_left -= count;
return true;
@@ -77,7 +87,11 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize)
{
pb_istream_t stream;
#ifdef PB_BUFFER_ONLY
stream.callback = NULL;
#else
stream.callback = &buf_read;
#endif
stream.state = buf;
stream.bytes_left = bufsize;
#ifndef PB_NO_ERRMSG
@@ -92,28 +106,60 @@ pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize)
static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
{
uint64_t temp;
bool status = pb_decode_varint(stream, &temp);
*dest = (uint32_t)temp;
return status;
uint8_t byte;
uint32_t result;
if (!pb_read(stream, &byte, 1))
return false;
if (!(byte & 0x80))
{
/* Quick case, 1 byte value */
result = byte;
}
else
{
/* Multibyte case */
uint8_t bitpos = 7;
result = byte & 0x7F;
do
{
if (bitpos >= 32)
PB_RETURN_ERROR(stream, "varint overflow");
if (!pb_read(stream, &byte, 1))
return false;
result |= (uint32_t)(byte & 0x7F) << bitpos;
bitpos = (uint8_t)(bitpos + 7);
} while (byte & 0x80);
}
*dest = result;
return true;
}
bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest)
{
uint8_t byte;
int bitpos = 0;
*dest = 0;
uint8_t bitpos = 0;
uint64_t result = 0;
while (bitpos < 64 && pb_read(stream, &byte, 1))
do
{
*dest |= (uint64_t)(byte & 0x7F) << bitpos;
bitpos += 7;
if (bitpos >= 64)
PB_RETURN_ERROR(stream, "varint overflow");
if (!(byte & 0x80))
return true;
}
if (!pb_read(stream, &byte, 1))
return false;
result |= (uint64_t)(byte & 0x7F) << bitpos;
bitpos = (uint8_t)(bitpos + 7);
} while (byte & 0x80);
PB_RETURN_ERROR(stream, "varint overflow");
*dest = result;
return true;
}
bool checkreturn pb_skip_varint(pb_istream_t *stream)
@@ -225,14 +271,18 @@ bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *su
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream)
{
stream->state = substream->state;
#ifndef PB_NO_ERRMSG
stream->errmsg = substream->errmsg;
#endif
}
/* Iterator for pb_field_t list */
typedef struct {
const pb_field_t *start; /* Start of the pb_field_t array */
const pb_field_t *current; /* Current position of the iterator */
int field_index; /* Zero-based index of the field. */
int required_field_index; /* Zero-based index that counts only the required fields */
unsigned field_index; /* Zero-based index of the field. */
unsigned required_field_index; /* Zero-based index that counts only the required fields */
void *dest_struct; /* Pointer to the destination structure to decode to */
void *pData; /* Pointer where to store current field value */
void *pSize; /* Pointer where to store the size of current array field */
@@ -278,7 +328,7 @@ static bool pb_field_next(pb_field_iterator_t *iter)
static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag)
{
int start = iter->field_index;
unsigned start = iter->field_index;
do {
if (iter->current->tag == tag)
@@ -311,7 +361,7 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
&& PB_LTYPE(iter->current->type) <= PB_LTYPE_LAST_PACKABLE)
{
/* Packed array */
bool status;
bool status = true;
size_t *size = (size_t*)iter->pSize;
pb_istream_t substream;
if (!pb_make_string_substream(stream, &substream))
@@ -321,11 +371,17 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
{
void *pItem = (uint8_t*)iter->pData + iter->current->data_size * (*size);
if (!func(&substream, iter->current, pItem))
return false;
{
status = false;
break;
}
(*size)++;
}
status = (substream.bytes_left == 0);
pb_close_string_substream(stream, &substream);
if (substream.bytes_left != 0)
PB_RETURN_ERROR(stream, "array overflow");
return status;
}
else
@@ -473,15 +529,34 @@ bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[
}
/* Check that all required fields were present. */
pb_field_init(&iter, fields, dest_struct);
do {
if (PB_HTYPE(iter.current->type) == PB_HTYPE_REQUIRED &&
iter.required_field_index < PB_MAX_REQUIRED_FIELDS &&
!(fields_seen[iter.required_field_index >> 3] & (1 << (iter.required_field_index & 7))))
{
/* First figure out the number of required fields by
* seeking to the end of the field array. Usually we
* are already close to end after decoding.
*/
unsigned req_field_count;
pb_type_t last_type;
unsigned i;
do {
req_field_count = iter.required_field_index;
last_type = iter.current->type;
} while (pb_field_next(&iter));
/* Fixup if last field was also required. */
if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED)
req_field_count++;
/* Check the whole bytes */
for (i = 0; i < (req_field_count >> 3); i++)
{
PB_RETURN_ERROR(stream, "missing required field");
if (fields_seen[i] != 0xFF)
PB_RETURN_ERROR(stream, "missing required field");
}
} while (pb_field_next(&iter));
/* Check the remaining bits */
if (fields_seen[req_field_count >> 3] != (0xFF >> (8 - (req_field_count & 7))))
PB_RETURN_ERROR(stream, "missing required field");
}
return true;
}
@@ -603,6 +678,9 @@ bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, voi
return false;
x->size = temp;
if (x->size < temp)
PB_RETURN_ERROR(stream, "size too large");
/* Check length, noting the space taken by the size_t header. */
if (x->size > field->data_size - offsetof(pb_bytes_array_t, bytes))
PB_RETURN_ERROR(stream, "bytes overflow");
@@ -613,12 +691,18 @@ bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, voi
bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
{
uint32_t size;
size_t alloc_size;
bool status;
if (!pb_decode_varint32(stream, &size))
return false;
alloc_size = size + 1;
if (alloc_size < size)
PB_RETURN_ERROR(stream, "size too large");
/* Check length, noting the null terminator */
if (size + 1 > field->data_size)
if (alloc_size > field->data_size)
PB_RETURN_ERROR(stream, "string overflow");
status = pb_read(stream, (uint8_t*)dest, size);

View File

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

View File

@@ -37,15 +37,22 @@ static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{
uint8_t *dest = (uint8_t*)stream->state;
memcpy(dest, buf, count);
stream->state = dest + count;
while (count--)
*dest++ = *buf++;
return true;
}
pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize)
{
pb_ostream_t stream;
#ifdef PB_BUFFER_ONLY
stream.callback = (void*)1; /* Just some marker value */
#else
stream.callback = &buf_write;
#endif
stream.state = buf;
stream.max_size = bufsize;
stream.bytes_written = 0;
@@ -58,9 +65,14 @@ bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count
{
if (stream->bytes_written + count > stream->max_size)
return false;
#ifdef PB_BUFFER_ONLY
if (!buf_write(stream, buf, count))
return false;
#else
if (!stream->callback(stream, buf, count))
return false;
#endif
}
stream->bytes_written += count;

View File

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

View File

@@ -3,11 +3,12 @@ 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_encode1 test_encode2 test_encode3 \
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_multiple_files test_cxxcompile test_options
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)
@@ -38,11 +39,26 @@ pb_encode.o: ../pb_encode.c $(DEPS)
pb_decode.o: ../pb_decode.c $(DEPS)
$(CC) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
# Test for compilability with c++ compiler
pb_encode.cxx.o: ../pb_encode.c $(DEPS)
$(CXX) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
pb_decode.cxx.o: ../pb_decode.c $(DEPS)
$(CXX) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
# Test for PB_BUF_ONLY compilation option
pb_encode.buf.o: ../pb_encode.c $(DEPS)
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
pb_decode.buf.o: ../pb_decode.c $(DEPS)
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
%.buf.o: %.c $(DEPS)
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) -c -o $@ $<
test_encode3_buf: test_encode3.buf.o pb_encode.buf.o alltypes.pb.o
$(CC) $(LDFLAGS) $^ -o $@
test_decode3_buf: test_decode3.buf.o pb_decode.buf.o alltypes.pb.o
$(CC) $(LDFLAGS) $^ -o $@
test_cxxcompile: pb_encode.cxx.o pb_decode.cxx.o
test_decode1: test_decode1.o pb_decode.o person.pb.o
test_decode2: test_decode2.o pb_decode.o person.pb.o
@@ -57,6 +73,9 @@ test_missing_fields: test_missing_fields.o pb_encode.o pb_decode.o missing_field
decode_unittests: decode_unittests.o pb_decode.o unittestproto.pb.o
encode_unittests: encode_unittests.o pb_encode.o unittestproto.pb.o
test_no_messages: no_messages.pb.h no_messages.pb.c no_messages.pb.o
test_funny_name: funny-proto+name.pb.h funny-proto+name.pb.o
bc_encode: bc_alltypes.pb.o pb_encode.o bc_encode.o
bc_decode: bc_alltypes.pb.o pb_decode.o bc_decode.o
%.pb: %.proto
protoc -I. -I../generator -I/usr/include -o$@ $<
@@ -95,6 +114,8 @@ run_unittests: $(TESTS)
./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

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

@@ -0,0 +1,326 @@
/* Automatically generated nanopb constant definitions */
#include "bc_alltypes.pb.h"
const char SubMessage_substuff1_default[17] = "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[17] = "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] = {
{1, PB_HTYPE_REQUIRED | PB_LTYPE_STRING,
offsetof(SubMessage, substuff1), 0,
pb_membersize(SubMessage, substuff1), 0,
&SubMessage_substuff1_default},
{2, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
pb_delta_end(SubMessage, substuff2, substuff1), 0,
pb_membersize(SubMessage, substuff2), 0,
&SubMessage_substuff2_default},
{3, PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED32,
pb_delta_end(SubMessage, substuff3, substuff2),
pb_delta(SubMessage, has_substuff3, substuff3),
pb_membersize(SubMessage, substuff3), 0,
&SubMessage_substuff3_default},
PB_LAST_FIELD
};
const pb_field_t AllTypes_fields[53] = {
{1, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
offsetof(AllTypes, req_int32), 0,
pb_membersize(AllTypes, req_int32), 0, 0},
{2, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, req_int64, req_int32), 0,
pb_membersize(AllTypes, req_int64), 0, 0},
{3, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, req_uint32, req_int64), 0,
pb_membersize(AllTypes, req_uint32), 0, 0},
{4, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, req_uint64, req_uint32), 0,
pb_membersize(AllTypes, req_uint64), 0, 0},
{5, PB_HTYPE_REQUIRED | PB_LTYPE_SVARINT,
pb_delta_end(AllTypes, req_sint32, req_uint64), 0,
pb_membersize(AllTypes, req_sint32), 0, 0},
{6, PB_HTYPE_REQUIRED | PB_LTYPE_SVARINT,
pb_delta_end(AllTypes, req_sint64, req_sint32), 0,
pb_membersize(AllTypes, req_sint64), 0, 0},
{7, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, req_bool, req_sint64), 0,
pb_membersize(AllTypes, req_bool), 0, 0},
{8, PB_HTYPE_REQUIRED | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, req_fixed32, req_bool), 0,
pb_membersize(AllTypes, req_fixed32), 0, 0},
{9, PB_HTYPE_REQUIRED | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, req_sfixed32, req_fixed32), 0,
pb_membersize(AllTypes, req_sfixed32), 0, 0},
{10, PB_HTYPE_REQUIRED | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, req_float, req_sfixed32), 0,
pb_membersize(AllTypes, req_float), 0, 0},
{11, PB_HTYPE_REQUIRED | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, req_fixed64, req_float), 0,
pb_membersize(AllTypes, req_fixed64), 0, 0},
{12, PB_HTYPE_REQUIRED | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, req_sfixed64, req_fixed64), 0,
pb_membersize(AllTypes, req_sfixed64), 0, 0},
{13, PB_HTYPE_REQUIRED | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, req_double, req_sfixed64), 0,
pb_membersize(AllTypes, req_double), 0, 0},
{14, PB_HTYPE_REQUIRED | PB_LTYPE_STRING,
pb_delta_end(AllTypes, req_string, req_double), 0,
pb_membersize(AllTypes, req_string), 0, 0},
{15, PB_HTYPE_REQUIRED | PB_LTYPE_BYTES,
pb_delta_end(AllTypes, req_bytes, req_string), 0,
pb_membersize(AllTypes, req_bytes), 0, 0},
{16, PB_HTYPE_REQUIRED | PB_LTYPE_SUBMESSAGE,
pb_delta_end(AllTypes, req_submsg, req_bytes), 0,
pb_membersize(AllTypes, req_submsg), 0,
&SubMessage_fields},
{17, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, req_enum, req_submsg), 0,
pb_membersize(AllTypes, req_enum), 0, 0},
{21, PB_HTYPE_ARRAY | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, rep_int32, req_enum),
pb_delta(AllTypes, rep_int32_count, rep_int32),
pb_membersize(AllTypes, rep_int32[0]),
pb_membersize(AllTypes, rep_int32) / pb_membersize(AllTypes, rep_int32[0]), 0},
{22, PB_HTYPE_ARRAY | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, rep_int64, rep_int32),
pb_delta(AllTypes, rep_int64_count, rep_int64),
pb_membersize(AllTypes, rep_int64[0]),
pb_membersize(AllTypes, rep_int64) / pb_membersize(AllTypes, rep_int64[0]), 0},
{23, PB_HTYPE_ARRAY | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, rep_uint32, rep_int64),
pb_delta(AllTypes, rep_uint32_count, rep_uint32),
pb_membersize(AllTypes, rep_uint32[0]),
pb_membersize(AllTypes, rep_uint32) / pb_membersize(AllTypes, rep_uint32[0]), 0},
{24, PB_HTYPE_ARRAY | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, rep_uint64, rep_uint32),
pb_delta(AllTypes, rep_uint64_count, rep_uint64),
pb_membersize(AllTypes, rep_uint64[0]),
pb_membersize(AllTypes, rep_uint64) / pb_membersize(AllTypes, rep_uint64[0]), 0},
{25, PB_HTYPE_ARRAY | PB_LTYPE_SVARINT,
pb_delta_end(AllTypes, rep_sint32, rep_uint64),
pb_delta(AllTypes, rep_sint32_count, rep_sint32),
pb_membersize(AllTypes, rep_sint32[0]),
pb_membersize(AllTypes, rep_sint32) / pb_membersize(AllTypes, rep_sint32[0]), 0},
{26, PB_HTYPE_ARRAY | PB_LTYPE_SVARINT,
pb_delta_end(AllTypes, rep_sint64, rep_sint32),
pb_delta(AllTypes, rep_sint64_count, rep_sint64),
pb_membersize(AllTypes, rep_sint64[0]),
pb_membersize(AllTypes, rep_sint64) / pb_membersize(AllTypes, rep_sint64[0]), 0},
{27, PB_HTYPE_ARRAY | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, rep_bool, rep_sint64),
pb_delta(AllTypes, rep_bool_count, rep_bool),
pb_membersize(AllTypes, rep_bool[0]),
pb_membersize(AllTypes, rep_bool) / pb_membersize(AllTypes, rep_bool[0]), 0},
{28, PB_HTYPE_ARRAY | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, rep_fixed32, rep_bool),
pb_delta(AllTypes, rep_fixed32_count, rep_fixed32),
pb_membersize(AllTypes, rep_fixed32[0]),
pb_membersize(AllTypes, rep_fixed32) / pb_membersize(AllTypes, rep_fixed32[0]), 0},
{29, PB_HTYPE_ARRAY | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, rep_sfixed32, rep_fixed32),
pb_delta(AllTypes, rep_sfixed32_count, rep_sfixed32),
pb_membersize(AllTypes, rep_sfixed32[0]),
pb_membersize(AllTypes, rep_sfixed32) / pb_membersize(AllTypes, rep_sfixed32[0]), 0},
{30, PB_HTYPE_ARRAY | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, rep_float, rep_sfixed32),
pb_delta(AllTypes, rep_float_count, rep_float),
pb_membersize(AllTypes, rep_float[0]),
pb_membersize(AllTypes, rep_float) / pb_membersize(AllTypes, rep_float[0]), 0},
{31, PB_HTYPE_ARRAY | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, rep_fixed64, rep_float),
pb_delta(AllTypes, rep_fixed64_count, rep_fixed64),
pb_membersize(AllTypes, rep_fixed64[0]),
pb_membersize(AllTypes, rep_fixed64) / pb_membersize(AllTypes, rep_fixed64[0]), 0},
{32, PB_HTYPE_ARRAY | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, rep_sfixed64, rep_fixed64),
pb_delta(AllTypes, rep_sfixed64_count, rep_sfixed64),
pb_membersize(AllTypes, rep_sfixed64[0]),
pb_membersize(AllTypes, rep_sfixed64) / pb_membersize(AllTypes, rep_sfixed64[0]), 0},
{33, PB_HTYPE_ARRAY | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, rep_double, rep_sfixed64),
pb_delta(AllTypes, rep_double_count, rep_double),
pb_membersize(AllTypes, rep_double[0]),
pb_membersize(AllTypes, rep_double) / pb_membersize(AllTypes, rep_double[0]), 0},
{34, PB_HTYPE_ARRAY | PB_LTYPE_STRING,
pb_delta_end(AllTypes, rep_string, rep_double),
pb_delta(AllTypes, rep_string_count, rep_string),
pb_membersize(AllTypes, rep_string[0]),
pb_membersize(AllTypes, rep_string) / pb_membersize(AllTypes, rep_string[0]), 0},
{35, PB_HTYPE_ARRAY | PB_LTYPE_BYTES,
pb_delta_end(AllTypes, rep_bytes, rep_string),
pb_delta(AllTypes, rep_bytes_count, rep_bytes),
pb_membersize(AllTypes, rep_bytes[0]),
pb_membersize(AllTypes, rep_bytes) / pb_membersize(AllTypes, rep_bytes[0]), 0},
{36, PB_HTYPE_ARRAY | PB_LTYPE_SUBMESSAGE,
pb_delta_end(AllTypes, rep_submsg, rep_bytes),
pb_delta(AllTypes, rep_submsg_count, rep_submsg),
pb_membersize(AllTypes, rep_submsg[0]),
pb_membersize(AllTypes, rep_submsg) / pb_membersize(AllTypes, rep_submsg[0]),
&SubMessage_fields},
{37, PB_HTYPE_ARRAY | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, rep_enum, rep_submsg),
pb_delta(AllTypes, rep_enum_count, rep_enum),
pb_membersize(AllTypes, rep_enum[0]),
pb_membersize(AllTypes, rep_enum) / pb_membersize(AllTypes, rep_enum[0]), 0},
{41, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, opt_int32, rep_enum),
pb_delta(AllTypes, has_opt_int32, opt_int32),
pb_membersize(AllTypes, opt_int32), 0,
&AllTypes_opt_int32_default},
{42, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, opt_int64, opt_int32),
pb_delta(AllTypes, has_opt_int64, opt_int64),
pb_membersize(AllTypes, opt_int64), 0,
&AllTypes_opt_int64_default},
{43, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, opt_uint32, opt_int64),
pb_delta(AllTypes, has_opt_uint32, opt_uint32),
pb_membersize(AllTypes, opt_uint32), 0,
&AllTypes_opt_uint32_default},
{44, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, opt_uint64, opt_uint32),
pb_delta(AllTypes, has_opt_uint64, opt_uint64),
pb_membersize(AllTypes, opt_uint64), 0,
&AllTypes_opt_uint64_default},
{45, PB_HTYPE_OPTIONAL | PB_LTYPE_SVARINT,
pb_delta_end(AllTypes, opt_sint32, opt_uint64),
pb_delta(AllTypes, has_opt_sint32, opt_sint32),
pb_membersize(AllTypes, opt_sint32), 0,
&AllTypes_opt_sint32_default},
{46, PB_HTYPE_OPTIONAL | PB_LTYPE_SVARINT,
pb_delta_end(AllTypes, opt_sint64, opt_sint32),
pb_delta(AllTypes, has_opt_sint64, opt_sint64),
pb_membersize(AllTypes, opt_sint64), 0,
&AllTypes_opt_sint64_default},
{47, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, opt_bool, opt_sint64),
pb_delta(AllTypes, has_opt_bool, opt_bool),
pb_membersize(AllTypes, opt_bool), 0,
&AllTypes_opt_bool_default},
{48, PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, opt_fixed32, opt_bool),
pb_delta(AllTypes, has_opt_fixed32, opt_fixed32),
pb_membersize(AllTypes, opt_fixed32), 0,
&AllTypes_opt_fixed32_default},
{49, PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, opt_sfixed32, opt_fixed32),
pb_delta(AllTypes, has_opt_sfixed32, opt_sfixed32),
pb_membersize(AllTypes, opt_sfixed32), 0,
&AllTypes_opt_sfixed32_default},
{50, PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED32,
pb_delta_end(AllTypes, opt_float, opt_sfixed32),
pb_delta(AllTypes, has_opt_float, opt_float),
pb_membersize(AllTypes, opt_float), 0,
&AllTypes_opt_float_default},
{51, PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, opt_fixed64, opt_float),
pb_delta(AllTypes, has_opt_fixed64, opt_fixed64),
pb_membersize(AllTypes, opt_fixed64), 0,
&AllTypes_opt_fixed64_default},
{52, PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, opt_sfixed64, opt_fixed64),
pb_delta(AllTypes, has_opt_sfixed64, opt_sfixed64),
pb_membersize(AllTypes, opt_sfixed64), 0,
&AllTypes_opt_sfixed64_default},
{53, PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED64,
pb_delta_end(AllTypes, opt_double, opt_sfixed64),
pb_delta(AllTypes, has_opt_double, opt_double),
pb_membersize(AllTypes, opt_double), 0,
&AllTypes_opt_double_default},
{54, PB_HTYPE_OPTIONAL | PB_LTYPE_STRING,
pb_delta_end(AllTypes, opt_string, opt_double),
pb_delta(AllTypes, has_opt_string, opt_string),
pb_membersize(AllTypes, opt_string), 0,
&AllTypes_opt_string_default},
{55, PB_HTYPE_OPTIONAL | PB_LTYPE_BYTES,
pb_delta_end(AllTypes, opt_bytes, opt_string),
pb_delta(AllTypes, has_opt_bytes, opt_bytes),
pb_membersize(AllTypes, opt_bytes), 0,
&AllTypes_opt_bytes_default},
{56, PB_HTYPE_OPTIONAL | PB_LTYPE_SUBMESSAGE,
pb_delta_end(AllTypes, opt_submsg, opt_bytes),
pb_delta(AllTypes, has_opt_submsg, opt_submsg),
pb_membersize(AllTypes, opt_submsg), 0,
&SubMessage_fields},
{57, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, opt_enum, opt_submsg),
pb_delta(AllTypes, has_opt_enum, opt_enum),
pb_membersize(AllTypes, opt_enum), 0,
&AllTypes_opt_enum_default},
{99, PB_HTYPE_REQUIRED | PB_LTYPE_VARINT,
pb_delta_end(AllTypes, end, opt_enum), 0,
pb_membersize(AllTypes, end), 0, 0},
PB_LAST_FIELD
};

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

@@ -0,0 +1,155 @@
/* Automatically generated nanopb header */
/* This is a file generated using nanopb-0.1.1.
* 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_BC_ALLTYPES_PB_H_
#define _PB_BC_ALLTYPES_PB_H_
#include <pb.h>
/* Enum definitions */
typedef enum {
MyEnum_Zero = 0,
MyEnum_First = 1,
MyEnum_Second = 2,
MyEnum_Truth = 42
} MyEnum;
/* Struct definitions */
typedef struct {
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 {
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[17];
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[17];
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];
#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 */
}
}

View File

View File

@@ -4,3 +4,4 @@ char fieldsize\[40\];
pb_callback_t int32_callback;
\sEnumValue1 = 1
Message5_EnumValue1
} pb_packed my_packed_struct;

View File

@@ -54,3 +54,20 @@ message Message5
}
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];
}