1/* This program parses an input string in a format a bit like JSON:
2 * {'foobar': 1234, 'xyz': 'abc', 'tree': [[[1, 2], 3], [4, 5]]}
3 * and encodes it as protobuf
4 *
5 * Note: The string parsing here is not in any way intended to be robust
6 *       nor safe against buffer overflows. It is just for this test.
7 */
8
9#include <pb_encode.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <string.h>
13#include "cyclic_callback.pb.h"
14
15static char *find_end_of_item(char *p)
16{
17    int depth = 0;
18    do {
19        if (*p == '[' || *p == '{') depth++;
20        if (*p == ']' || *p == '}') depth--;
21        p++;
22    } while (depth > 0 || (*p != ',' && *p != '}'));
23
24    if (*p == '}')
25        return p; /* End of parent dict */
26
27    p++;
28    while (*p == ' ') p++;
29    return p;
30}
31
32/* Parse a tree in format [[1 2] 3] and encode it directly to protobuf */
33static bool encode_tree(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
34{
35    TreeNode tree = TreeNode_init_zero;
36    char *p = (char*)*arg;
37
38    if (*p == '[')
39    {
40        /* This is a tree branch */
41        p++;
42        tree.left.funcs.encode = encode_tree;
43        tree.left.arg = p;
44
45        p = find_end_of_item(p);
46        tree.right.funcs.encode = encode_tree;
47        tree.right.arg = p;
48    }
49    else
50    {
51        /* This is a leaf node */
52        tree.has_leaf = true;
53        tree.leaf = atoi(p);
54    }
55
56    return pb_encode_tag_for_field(stream, field) &&
57           pb_encode_submessage(stream, TreeNode_fields, &tree);
58}
59
60/* Parse a dictionary in format {'name': value} and encode it directly to protobuf */
61static bool encode_dictionary(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
62{
63    int textlen;
64    char *p = (char*)*arg;
65    if (*p == '{') p++;
66    while (*p != '}')
67    {
68        KeyValuePair pair = KeyValuePair_init_zero;
69
70        if (*p != '\'')
71            PB_RETURN_ERROR(stream, "invalid key, missing quote");
72
73        p++; /* Starting quote of key */
74        textlen = strchr(p, '\'') - p;
75        strncpy(pair.key, p, textlen);
76        pair.key[textlen] = 0;
77        p += textlen + 2;
78
79        while (*p == ' ') p++;
80
81        if (*p == '[')
82        {
83            /* Value is a tree */
84            pair.treeValue.funcs.encode = encode_tree;
85            pair.treeValue.arg = p;
86        }
87        else if (*p == '\'')
88        {
89            /* Value is a string */
90            pair.has_stringValue = true;
91            p++;
92            textlen = strchr(p, '\'') - p;
93            strncpy(pair.stringValue, p, textlen);
94            pair.stringValue[textlen] = 0;
95        }
96        else if (*p == '{')
97        {
98            /* Value is a dictionary */
99            pair.has_dictValue = true;
100            pair.dictValue.dictItem.funcs.encode = encode_dictionary;
101            pair.dictValue.dictItem.arg = p;
102        }
103        else
104        {
105            /* Value is integer */
106            pair.has_intValue = true;
107            pair.intValue = atoi(p);
108        }
109
110        p = find_end_of_item(p);
111
112        if (!pb_encode_tag_for_field(stream, field))
113            return false;
114
115        if (!pb_encode_submessage(stream, KeyValuePair_fields, &pair))
116            return false;
117    }
118
119    return true;
120}
121
122
123int main(int argc, char *argv[])
124{
125    uint8_t buffer[256];
126    pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
127    Dictionary dict = Dictionary_init_zero;
128
129    if (argc <= 1)
130    {
131        fprintf(stderr, "Usage: %s \"{'foobar': 1234, ...}\"\n", argv[0]);
132        return 1;
133    }
134
135    dict.dictItem.funcs.encode = encode_dictionary;
136    dict.dictItem.arg = argv[1];
137
138    if (!pb_encode(&stream, Dictionary_fields, &dict))
139    {
140        fprintf(stderr, "Encoding error: %s\n", PB_GET_ERROR(&stream));
141        return 1;
142    }
143
144    fwrite(buffer, 1, stream.bytes_written, stdout);
145    return 0;
146}
147
148
149