Add generator option sort_by_tag (#542)

For historical reasons fields in .pb.h have been ordered by tag number.
As of 0.4.0 it is no longer necessary, and can now be disabled.

This may become the default in 0.5.0.
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index 07beed3..91c8975 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -362,6 +362,7 @@
         self.fixed_count = False
         self.callback_datatype = field_options.callback_datatype
         self.math_include_required = False
+        self.sort_by_tag = field_options.sort_by_tag
 
         if field_options.type == nanopb_pb2.FT_INLINE:
             # Before nanopb-0.3.8, fixed length bytes arrays were specified
@@ -871,7 +872,7 @@
 # ---------------------------------------------------------------------------
 
 class OneOf(Field):
-    def __init__(self, struct_name, oneof_desc):
+    def __init__(self, struct_name, oneof_desc, oneof_options):
         self.struct_name = struct_name
         self.name = oneof_desc.name
         self.ctype = 'union'
@@ -880,7 +881,8 @@
         self.allocation = 'ONEOF'
         self.default = None
         self.rules = 'ONEOF'
-        self.anonymous = False
+        self.anonymous = oneof_options.anonymous_oneof
+        self.sort_by_tag = oneof_options.sort_by_tag
         self.has_msg_cb = False
 
     def add_field(self, field):
@@ -888,7 +890,9 @@
         field.rules = 'ONEOF'
         field.anonymous = self.anonymous
         self.fields.append(field)
-        self.fields.sort(key = lambda f: f.tag)
+
+        if self.sort_by_tag:
+            self.fields.sort()
 
         if field.pbtype == 'MSG_W_CB':
             self.has_msg_cb = True
@@ -1019,11 +1023,8 @@
                 elif oneof_options.type == nanopb_pb2.FT_IGNORE:
                     pass # No union and skip fields also
                 else:
-                    oneof = OneOf(self.name, f)
-                    if oneof_options.anonymous_oneof:
-                        oneof.anonymous = True
+                    oneof = OneOf(self.name, f, oneof_options)
                     self.oneofs[i] = oneof
-                    self.fields.append(oneof)
         else:
             sys.stderr.write('Note: This Python protobuf library has no OneOf support\n')
 
@@ -1038,6 +1039,9 @@
                 f.oneof_index not in no_unions):
                 if f.oneof_index in self.oneofs:
                     self.oneofs[f.oneof_index].add_field(field)
+
+                    if self.oneofs[f.oneof_index] not in self.fields:
+                        self.fields.append(self.oneofs[f.oneof_index])
             else:
                 self.fields.append(field)
                 if field.math_include_required:
@@ -1049,6 +1053,9 @@
             if field_options.type != nanopb_pb2.FT_IGNORE:
                 self.fields.append(ExtensionRange(self.name, range_start, field_options))
 
+        if message_options.sort_by_tag:
+            self.fields.sort()
+
     def get_dependencies(self):
         '''Get list of type names that this structure refers to.'''
         deps = []
@@ -1064,7 +1071,7 @@
             # Therefore add a dummy field if an empty message occurs.
             result += '    char dummy_field;'
 
-        result += '\n'.join([str(f) for f in sorted(self.fields)])
+        result += '\n'.join([str(f) for f in self.fields])
 
         if Globals.protoc_insertion_points:
             result += '\n/* @@protoc_insertion_point(struct:%s) */' % self.name
@@ -1090,7 +1097,7 @@
             return '{0}'
 
         parts = []
-        for field in sorted(self.fields):
+        for field in self.fields:
             parts.append(field.get_initializer(null_init))
         return '{' + ', '.join(parts) + '}'
 
diff --git a/generator/proto/nanopb.proto b/generator/proto/nanopb.proto
index 6a3c620..96e24b4 100644
--- a/generator/proto/nanopb.proto
+++ b/generator/proto/nanopb.proto
@@ -141,6 +141,11 @@
   
   // Override type of the field in generated C code. Only to be used with related field types
   optional google.protobuf.FieldDescriptorProto.Type type_override = 27;
+
+  // Due to historical reasons, nanopb orders fields in structs by their tag number
+  // instead of the order in .proto. Set this to false to keep the .proto order.
+  // The default value will probably change to false in nanopb-0.5.0.
+  optional bool sort_by_tag = 28 [default = true];
 }
 
 // Extensions to protoc 'Descriptor' type in order to define options
diff --git a/tests/sort_by_tag/SConscript b/tests/sort_by_tag/SConscript
new file mode 100644
index 0000000..96a746d
--- /dev/null
+++ b/tests/sort_by_tag/SConscript
@@ -0,0 +1,8 @@
+# Test sort_by_tag generator option
+
+Import("env")
+
+env.NanopbProto(["sort_by_tag.proto", "sort_by_tag.options"])
+test = env.Program(["sort_by_tag.c", "sort_by_tag.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+env.RunTest(test)
+
diff --git a/tests/sort_by_tag/sort_by_tag.c b/tests/sort_by_tag/sort_by_tag.c
new file mode 100644
index 0000000..bdb1e3e
--- /dev/null
+++ b/tests/sort_by_tag/sort_by_tag.c
@@ -0,0 +1,55 @@
+#include "sort_by_tag.pb.h"
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "unittests.h"
+
+int main()
+{
+    int status = 0;
+    size_t msglen;
+    pb_byte_t buf[256];
+    
+    {
+        pb_ostream_t ostream = pb_ostream_from_buffer(buf, sizeof(buf));
+        Unsorted msg = Unsorted_init_zero;
+        COMMENT("Test encoding with unsorted structure");
+
+        TEST(&msg.first < &msg.oneof.second);
+        TEST(&msg.oneof.second < &msg.last);
+
+        msg.first = 101;
+        msg.which_oneof = Unsorted_second_tag;
+        msg.oneof.second = 102;
+        msg.last = 103;
+
+        if (!pb_encode(&ostream, Unsorted_fields, &msg))
+        {
+            fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&ostream));
+            return 1;
+        }
+
+        msglen = ostream.bytes_written;
+    }
+
+    {
+        pb_istream_t istream = pb_istream_from_buffer(buf, msglen);
+        Sorted msg = Sorted_init_zero;
+        COMMENT("Test decoding with sorted structure");
+
+        if (!pb_decode(&istream, Sorted_fields, &msg))
+        {
+            fprintf(stderr, "Decoding failed: %s\n", PB_GET_ERROR(&istream));
+            return 2;
+        }
+
+        TEST(msg.first == 101);
+        TEST(msg.which_oneof == Sorted_second_tag);
+        TEST(msg.oneof.second == 102);
+        TEST(msg.last == 103);
+
+        TEST(&msg.first > &msg.oneof.second);
+        TEST(&msg.oneof.second > &msg.last);
+    }
+
+    return status;
+}
\ No newline at end of file
diff --git a/tests/sort_by_tag/sort_by_tag.options b/tests/sort_by_tag/sort_by_tag.options
new file mode 100644
index 0000000..b9b1d8e
--- /dev/null
+++ b/tests/sort_by_tag/sort_by_tag.options
@@ -0,0 +1,2 @@
+Unsorted	sort_by_tag:false
+Sorted		sort_by_tag:true
diff --git a/tests/sort_by_tag/sort_by_tag.proto b/tests/sort_by_tag/sort_by_tag.proto
new file mode 100644
index 0000000..95596bf
--- /dev/null
+++ b/tests/sort_by_tag/sort_by_tag.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+message Unsorted
+{
+    uint32 first = 99;
+    oneof oneof {
+        uint32 second = 80;
+        uint32 third = 50;
+    }
+    uint32 last = 1;
+}
+
+message Sorted
+{
+    uint32 first = 99;
+    oneof oneof {
+        uint32 second = 80;
+        uint32 third = 50;
+    }
+    uint32 last = 1;
+}