aboutsummaryrefslogtreecommitdiffstats
path: root/tests/cyclic_messages/encode_cyclic_callback.c
blob: 7f67e70c6326bbd4a6ff22ef97befca89d89f735 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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;
}