1 /* Decode a message using callbacks inside oneof fields */
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <pb_decode.h>
7 #include <assert.h>
8 #include "oneof.pb.h"
9 #include "test_helpers.h"
10 #include "unittests.h"
11 
12 /* This is a nanopb-0.4 style global callback, that is referred by function name
13  * and does not have to be bound separately to the message. It also allows defining
14  * a custom data type for the field in the structure.
15  */
SubMsg3_callback(pb_istream_t * istream,pb_ostream_t * ostream,const pb_field_t * field)16 bool SubMsg3_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
17 {
18     if (istream && field->tag == SubMsg3_strvalue_tag)
19     {
20         /* We could e.g. malloc some memory and assign it to our custom datatype
21          * in the message structure here, accessible by field->pData. But in
22          * this example we just print the string directly.
23          */
24 
25         uint8_t buffer[64];
26         int strlen = istream->bytes_left;
27 
28         if (strlen > sizeof(buffer) - 1)
29             return false;
30 
31         buffer[strlen] = '\0';
32 
33         if (!pb_read(istream, buffer, strlen))
34             return false;
35 
36         printf("  strvalue: \"%s\"\n", buffer);
37     }
38 
39     return true;
40 }
41 
42 /* The two callbacks below are traditional callbacks that use function pointers
43  * defined in pb_callback_t.
44  */
print_int32(pb_istream_t * stream,const pb_field_t * field,void ** arg)45 bool print_int32(pb_istream_t *stream, const pb_field_t *field, void **arg)
46 {
47     uint64_t value;
48     if (!pb_decode_varint(stream, &value))
49         return false;
50 
51     printf((char*)*arg, (int)value);
52     return true;
53 }
54 
print_string(pb_istream_t * stream,const pb_field_t * field,void ** arg)55 bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
56 {
57     uint8_t buffer[64];
58     int strlen = stream->bytes_left;
59 
60     if (strlen > sizeof(buffer) - 1)
61         return false;
62 
63     buffer[strlen] = '\0';
64 
65     if (!pb_read(stream, buffer, strlen))
66         return false;
67 
68     /* Print the string, in format comparable with protoc --decode.
69      * Format comes from the arg defined in main().
70      */
71     printf((char*)*arg, buffer);
72     return true;
73 }
74 
75 /* The callback below is a message-level callback which is called before each
76  * submessage is encoded. It is used to set the pb_callback_t callbacks inside
77  * the submessage. The reason we need this is that different submessages share
78  * storage inside oneof union, and before we know the message type we can't set
79  * the callbacks without overwriting each other.
80  */
msg_callback(pb_istream_t * stream,const pb_field_t * field,void ** arg)81 bool msg_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
82 {
83     /* Print the prefix field before the submessages.
84      * This also demonstrates how to access the top level message fields
85      * from callbacks.
86      */
87     OneOfMessage *topmsg = field->message;
88     printf("prefix: %d\n", (int)topmsg->prefix);
89 
90     if (field->tag == OneOfMessage_submsg1_tag)
91     {
92         SubMsg1 *msg = field->pData;
93         printf("submsg1 {\n");
94         msg->array.funcs.decode = print_int32;
95         msg->array.arg = "  array: %d\n";
96     }
97     else if (field->tag == OneOfMessage_submsg2_tag)
98     {
99         SubMsg2 *msg = field->pData;
100         printf("submsg2 {\n");
101         msg->strvalue.funcs.decode = print_string;
102         msg->strvalue.arg = "  strvalue: \"%s\"\n";
103     }
104     else if (field->tag == OneOfMessage_submsg3_tag)
105     {
106         /* Because SubMsg3 callback is bound by function name, we do not
107          * need to initialize anything here. But we just print a string
108          * to get protoc-equivalent formatted output from the testcase.
109          */
110         printf("submsg3 {\n");
111     }
112 
113     /* Once we return true, pb_dec_submessage() will go on to decode the
114      * submessage contents. But if we want, we can also decode it ourselves
115      * above and leave stream->bytes_left at 0 value, inhibiting automatic
116      * decoding.
117      */
118     return true;
119 }
120 
main(int argc,char ** argv)121 int main(int argc, char **argv)
122 {
123     uint8_t buffer[256];
124     OneOfMessage msg = OneOfMessage_init_zero;
125     pb_istream_t stream;
126     size_t count;
127 
128     SET_BINARY_MODE(stdin);
129     count = fread(buffer, 1, sizeof(buffer), stdin);
130 
131     if (!feof(stdin))
132     {
133         fprintf(stderr, "Message does not fit in buffer\n");
134         return 1;
135     }
136 
137     /* Set up the cb_values callback, which will in turn set up the callbacks
138      * for each oneof field once the field tag is known. */
139     msg.cb_values.funcs.decode = msg_callback;
140     stream = pb_istream_from_buffer(buffer, count);
141     if (!pb_decode(&stream, OneOfMessage_fields, &msg))
142     {
143         fprintf(stderr, "Decoding failed: %s\n", PB_GET_ERROR(&stream));
144         return 1;
145     }
146 
147     /* This is just printing for the test case logic */
148     if (msg.which_values == OneOfMessage_intvalue_tag)
149     {
150         printf("prefix: %d\n", (int)msg.prefix);
151         printf("intvalue: %d\n", (int)msg.values.intvalue);
152     }
153     else if (msg.which_values == OneOfMessage_strvalue_tag)
154     {
155         printf("prefix: %d\n", (int)msg.prefix);
156         printf("strvalue: \"%s\"\n", msg.values.strvalue);
157     }
158     else if (msg.which_values == OneOfMessage_submsg3_tag &&
159              msg.values.submsg3.which_values == SubMsg3_intvalue_tag)
160     {
161         printf("  intvalue: %d\n", (int)msg.values.submsg3.values.intvalue);
162         printf("}\n");
163     }
164     else
165     {
166         printf("}\n");
167     }
168     printf("suffix: %d\n", (int)msg.suffix);
169 
170     assert(msg.prefix == 123);
171     assert(msg.suffix == 321);
172 
173     return 0;
174 }
175