summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rwxr-xr-xgenerator/nanopb_generator.py20
-rw-r--r--pb.h2
-rw-r--r--pb_common.c6
-rw-r--r--tests/regression/issue_229/SConscript13
-rw-r--r--tests/regression/issue_229/multiple_oneof.c35
-rw-r--r--tests/regression/issue_229/multiple_oneof.proto11
7 files changed, 80 insertions, 9 deletions
diff --git a/README.md b/README.md
index a32d6ca2..459eaf84 100644
--- a/README.md
+++ b/README.md
@@ -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:
diff --git a/pb.h b/pb.h
index 2cf5c4dc..f68d1d65 100644
--- a/pb.h
+++ b/pb.h
@@ -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;
+ }
+}
+