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