Fix cyclic messages support in generator. Beginnings of test.
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>
Thu, 28 Aug 2014 18:23:28 +0000 (21:23 +0300)
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>
Thu, 28 Aug 2014 18:23:28 +0000 (21:23 +0300)
Update issue 130
Status: Started

generator/nanopb_generator.py
tests/cyclic_messages/SConscript [new file with mode: 0644]
tests/cyclic_messages/cyclic.proto [new file with mode: 0644]
tests/cyclic_messages/cyclic_callback.options [new file with mode: 0644]
tests/cyclic_messages/encode_cyclic_callback.c [new file with mode: 0644]

index 79dd198..56dfdb0 100755 (executable)
@@ -546,7 +546,7 @@ class Message:
 
     def get_dependencies(self):
         '''Get list of type names that this structure refers to.'''
-        return [str(field.ctype) for field in self.fields]
+        return [str(field.ctype) for field in self.fields if field.allocation == 'STATIC']
     
     def __str__(self):
         result = 'typedef struct _%s {\n' % self.name
diff --git a/tests/cyclic_messages/SConscript b/tests/cyclic_messages/SConscript
new file mode 100644 (file)
index 0000000..c782001
--- /dev/null
@@ -0,0 +1,11 @@
+Import("env")
+
+# Encode cyclic messages with callback fields
+
+c = Copy("$TARGET", "$SOURCE")
+env.Command("cyclic_callback.proto", "cyclic.proto", c)
+env.NanopbProto(["cyclic_callback", "cyclic_callback.options"])
+
+enc_callback = env.Program(["encode_cyclic_callback.c", "cyclic_callback.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+
+
diff --git a/tests/cyclic_messages/cyclic.proto b/tests/cyclic_messages/cyclic.proto
new file mode 100644 (file)
index 0000000..a9d158c
--- /dev/null
@@ -0,0 +1,25 @@
+// Test structures with cyclic references.
+// These can only be handled in pointer/callback mode,
+// see associated .options files.
+
+message TreeNode
+{
+    optional int32 leaf = 1;
+    optional TreeNode left = 2;
+    optional TreeNode right = 3;
+}
+
+message Dictionary
+{
+    repeated KeyValuePair dictItem = 1;
+}
+
+message KeyValuePair
+{
+    required string key = 1;
+    optional string stringValue = 2;
+    optional int32 intValue = 3;
+    optional Dictionary dictValue = 4;
+    optional TreeNode treeValue = 5;
+}
+
diff --git a/tests/cyclic_messages/cyclic_callback.options b/tests/cyclic_messages/cyclic_callback.options
new file mode 100644 (file)
index 0000000..fd4e1e1
--- /dev/null
@@ -0,0 +1,7 @@
+TreeNode.left               type:FT_CALLBACK
+TreeNode.right              type:FT_CALLBACK
+
+Dictionary.data             type:FT_CALLBACK
+KeyValuePair.key            max_size:8
+KeyValuePair.stringValue    max_size:8
+KeyValuePair.treeValue      type:FT_CALLBACK
diff --git a/tests/cyclic_messages/encode_cyclic_callback.c b/tests/cyclic_messages/encode_cyclic_callback.c
new file mode 100644 (file)
index 0000000..7f67e70
--- /dev/null
@@ -0,0 +1,148 @@
+/* This program parses an input string in a format a bit like JSON:
+ * {'foobar': 1234, 'xyz': 'abc', 'tree': [[[1, 2], 3], [4, 5]]}
+ * and encodes it as protobuf
+ *
+ * Note: The string parsing here is not in any way intended to be robust
+ *       nor safe against buffer overflows. It is just for this test.
+ */
+
+#include <pb_encode.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "cyclic_callback.pb.h"
+
+static char *find_end_of_item(char *p)
+{
+    int depth = 0;
+    do {
+        if (*p == '[' || *p == '{') depth++;
+        if (*p == ']' || *p == '}') depth--;
+        p++;
+    } while (depth > 0 || (*p != ',' && *p != '}'));
+    
+    if (*p == '}')
+        return p; /* End of parent dict */
+    
+    p++;
+    while (*p == ' ') p++;
+    return p;
+}
+
+/* Parse a tree in format [[1 2] 3] and encode it directly to protobuf */
+static bool encode_tree(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    TreeNode tree = TreeNode_init_zero;
+    char *p = (char*)*arg;
+    
+    if (*p == '[')
+    {
+        /* This is a tree branch */
+        p++;
+        tree.left.funcs.encode = encode_tree;
+        tree.left.arg = p;
+        
+        p = find_end_of_item(p);
+        tree.right.funcs.encode = encode_tree;
+        tree.right.arg = p;
+    }
+    else
+    {
+        /* This is a leaf node */
+        tree.has_leaf = true;
+        tree.leaf = atoi(p);
+    }
+    
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, TreeNode_fields, &tree);
+}
+
+/* Parse a dictionary in format {'name': value} and encode it directly to protobuf */
+static bool encode_dictionary(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    int textlen;
+    char *p = (char*)*arg;
+    if (*p == '{') p++;
+    while (*p != '}')
+    {
+        KeyValuePair pair = KeyValuePair_init_zero;
+        
+        if (*p != '\'')
+            PB_RETURN_ERROR(stream, "invalid key, missing quote");
+        
+        p++; /* Starting quote of key */
+        textlen = strchr(p, '\'') - p;
+        strncpy(pair.key, p, textlen);
+        pair.key[textlen] = 0;
+        p += textlen + 2;
+        
+        while (*p == ' ') p++;
+        
+        if (*p == '[')
+        {
+            /* Value is a tree */
+            pair.treeValue.funcs.encode = encode_tree;
+            pair.treeValue.arg = p;
+        }
+        else if (*p == '\'')
+        {
+            /* Value is a string */
+            pair.has_stringValue = true;
+            p++;
+            textlen = strchr(p, '\'') - p;
+            strncpy(pair.stringValue, p, textlen);
+            pair.stringValue[textlen] = 0;
+        }
+        else if (*p == '{')
+        {
+            /* Value is a dictionary */
+            pair.has_dictValue = true;
+            pair.dictValue.dictItem.funcs.encode = encode_dictionary;
+            pair.dictValue.dictItem.arg = p;
+        }
+        else
+        {
+            /* Value is integer */
+            pair.has_intValue = true;
+            pair.intValue = atoi(p);
+        }
+        
+        p = find_end_of_item(p);
+        
+        if (!pb_encode_tag_for_field(stream, field))
+            return false;
+        
+        if (!pb_encode_submessage(stream, KeyValuePair_fields, &pair))
+            return false;
+    }
+
+    return true;
+}
+
+
+int main(int argc, char *argv[])
+{
+    uint8_t buffer[256];
+    pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+    Dictionary dict = Dictionary_init_zero;
+    
+    if (argc <= 1)
+    {
+        fprintf(stderr, "Usage: %s \"{'foobar': 1234, ...}\"\n", argv[0]);
+        return 1;
+    }
+    
+    dict.dictItem.funcs.encode = encode_dictionary;
+    dict.dictItem.arg = argv[1];
+
+    if (!pb_encode(&stream, Dictionary_fields, &dict))
+    {
+        fprintf(stderr, "Encoding error: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+    
+    fwrite(buffer, 1, stream.bytes_written, stdout);
+    return 0;
+}
+
+