diff options
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | generator/nanopb_generator.py | 20 | ||||
-rw-r--r-- | pb.h | 2 | ||||
-rw-r--r-- | pb_common.c | 6 | ||||
-rw-r--r-- | tests/regression/issue_229/SConscript | 13 | ||||
-rw-r--r-- | tests/regression/issue_229/multiple_oneof.c | 35 | ||||
-rw-r--r-- | tests/regression/issue_229/multiple_oneof.proto | 11 |
7 files changed, 80 insertions, 9 deletions
@@ -19,7 +19,7 @@ Using the nanopb library To use the nanopb library, you need to do two things: 1. Compile your .proto files for nanopb, using protoc. -2. Include pb_encode.c and pb_decode.c in your project. +2. Include pb_encode.c, pb_decode.c and pb_common.c in your project. The easiest way to get started is to study the project in "examples/simple". It contains a Makefile, which should work directly under most Linux systems. diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index a2ee22db..6e5ebaf9 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -509,9 +509,10 @@ class Field: identifier = '%s_%s_tag' % (self.struct_name, self.name) return '#define %-40s %d\n' % (identifier, self.tag) - def pb_field_t(self, prev_field_name): + def pb_field_t(self, prev_field_name, union_index = None): '''Return the pb_field_t initializer to use in the constant array. - prev_field_name is the name of the previous field or None. + prev_field_name is the name of the previous field or None. For OneOf + unions, union_index is the index of this field inside the OneOf. ''' if self.rules == 'ONEOF': @@ -526,7 +527,14 @@ class Field: result += '%-8s, ' % self.pbtype result += '%s, ' % self.rules result += '%-8s, ' % (self.allocation if not self.inline else "INLINE") - result += '%s, ' % ("FIRST" if not prev_field_name else "OTHER") + + if union_index is not None and union_index > 0: + result += 'UNION, ' + elif prev_field_name is None: + result += 'FIRST, ' + else: + result += 'OTHER, ' + result += '%s, ' % self.struct_name result += '%s, ' % self.name result += '%s, ' % (prev_field_name or self.name) @@ -767,8 +775,10 @@ class OneOf(Field): return ''.join([f.tags() for f in self.fields]) def pb_field_t(self, prev_field_name): - result = ',\n'.join([f.pb_field_t(prev_field_name) for f in self.fields]) - return result + parts = [] + for union_index, field in enumerate(self.fields): + parts.append(field.pb_field_t(prev_field_name, union_index)) + return ',\n'.join(parts) def get_last_field_name(self): if self.anonymous: @@ -393,6 +393,8 @@ struct pb_extension_s { #define PB_DATAOFFSET_FIRST(st, m1, m2) (offsetof(st, m1)) /* data_offset for subsequent fields */ #define PB_DATAOFFSET_OTHER(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2)) +/* data offset for subsequent fields inside an union (oneof) */ +#define PB_DATAOFFSET_UNION(st, m1, m2) (PB_SIZE_MAX) /* Choose first/other based on m1 == m2 (deprecated, remains for backwards compatibility) */ #define PB_DATAOFFSET_CHOOSE(st, m1, m2) (int)(offsetof(st, m1) == offsetof(st, m2) \ ? PB_DATAOFFSET_FIRST(st, m1, m2) \ diff --git a/pb_common.c b/pb_common.c index 385c0193..4fb7186b 100644 --- a/pb_common.c +++ b/pb_common.c @@ -42,11 +42,11 @@ bool pb_field_iter_next(pb_field_iter_t *iter) size_t prev_size = prev_field->data_size; if (PB_HTYPE(prev_field->type) == PB_HTYPE_ONEOF && - PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF) + PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF && + iter->pos->data_offset == PB_SIZE_MAX) { /* Don't advance pointers inside unions */ - prev_size = 0; - iter->pData = (char*)iter->pData - prev_field->data_offset; + return true; } else if (PB_ATYPE(prev_field->type) == PB_ATYPE_STATIC && PB_HTYPE(prev_field->type) == PB_HTYPE_REPEATED) diff --git a/tests/regression/issue_229/SConscript b/tests/regression/issue_229/SConscript new file mode 100644 index 00000000..b0f8376d --- /dev/null +++ b/tests/regression/issue_229/SConscript @@ -0,0 +1,13 @@ +# Regression test for Issue 229: problem encoding message that has +# multiple oneof fields +Import('env') + +env.NanopbProto('multiple_oneof') + +p = env.Program(["multiple_oneof.c", + "multiple_oneof.pb.c", + "$COMMON/pb_decode.o", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) + diff --git a/tests/regression/issue_229/multiple_oneof.c b/tests/regression/issue_229/multiple_oneof.c new file mode 100644 index 00000000..902248d0 --- /dev/null +++ b/tests/regression/issue_229/multiple_oneof.c @@ -0,0 +1,35 @@ +#include "multiple_oneof.pb.h" +#include <unittests.h> +#include <pb_encode.h> +#include <pb_decode.h> + +int main() +{ + int status = 0; + uint8_t buf[128]; + size_t msglen; + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + MainMessage msg = MainMessage_init_zero; + msg.which_oneof1 = MainMessage_oneof1_uint32_tag; + msg.oneof1.oneof1_uint32 = 1234; + msg.which_oneof2 = MainMessage_oneof2_uint32_tag; + msg.oneof2.oneof2_uint32 = 5678; + TEST(pb_encode(&stream, MainMessage_fields, &msg)); + msglen = stream.bytes_written; + } + + { + pb_istream_t stream = pb_istream_from_buffer(buf, msglen); + MainMessage msg = MainMessage_init_zero; + TEST(pb_decode(&stream, MainMessage_fields, &msg)); + TEST(msg.which_oneof1 == MainMessage_oneof1_uint32_tag); + TEST(msg.oneof1.oneof1_uint32 == 1234); + TEST(msg.which_oneof2 == MainMessage_oneof2_uint32_tag); + TEST(msg.oneof2.oneof2_uint32 == 5678); + } + + return status; +} + diff --git a/tests/regression/issue_229/multiple_oneof.proto b/tests/regression/issue_229/multiple_oneof.proto new file mode 100644 index 00000000..22373e1d --- /dev/null +++ b/tests/regression/issue_229/multiple_oneof.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +message MainMessage { + oneof oneof1 { + uint32 oneof1_uint32 = 1; + } + oneof oneof2 { + uint32 oneof2_uint32 = 2; + } +} + |