Fix cyclic messages support in generator. Beginnings of test.
Update issue 130 Status: Started
This commit is contained in:
@@ -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
|
||||
|
||||
11
tests/cyclic_messages/SConscript
Normal file
11
tests/cyclic_messages/SConscript
Normal file
@@ -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"])
|
||||
|
||||
|
||||
25
tests/cyclic_messages/cyclic.proto
Normal file
25
tests/cyclic_messages/cyclic.proto
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
7
tests/cyclic_messages/cyclic_callback.options
Normal file
7
tests/cyclic_messages/cyclic_callback.options
Normal file
@@ -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
|
||||
148
tests/cyclic_messages/encode_cyclic_callback.c
Normal file
148
tests/cyclic_messages/encode_cyclic_callback.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user