Line data Source code
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 : */
16 1 : bool SubMsg3_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
17 : {
18 1 : 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 1 : int strlen = istream->bytes_left;
27 :
28 1 : if (strlen > sizeof(buffer) - 1)
29 0 : return false;
30 :
31 1 : buffer[strlen] = '\0';
32 :
33 1 : if (!pb_read(istream, buffer, strlen))
34 0 : return false;
35 :
36 1 : printf(" strvalue: \"%s\"\n", buffer);
37 : }
38 :
39 1 : return true;
40 : }
41 :
42 : /* The two callbacks below are traditional callbacks that use function pointers
43 : * defined in pb_callback_t.
44 : */
45 15 : bool print_int32(pb_istream_t *stream, const pb_field_t *field, void **arg)
46 : {
47 : uint64_t value;
48 15 : if (!pb_decode_varint(stream, &value))
49 0 : return false;
50 :
51 15 : printf((char*)*arg, (int)value);
52 15 : return true;
53 : }
54 :
55 1 : bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
56 : {
57 : uint8_t buffer[64];
58 1 : int strlen = stream->bytes_left;
59 :
60 1 : if (strlen > sizeof(buffer) - 1)
61 0 : return false;
62 :
63 1 : buffer[strlen] = '\0';
64 :
65 1 : if (!pb_read(stream, buffer, strlen))
66 0 : return false;
67 :
68 : /* Print the string, in format comparable with protoc --decode.
69 : * Format comes from the arg defined in main().
70 : */
71 1 : printf((char*)*arg, buffer);
72 1 : 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 : */
81 4 : 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 4 : OneOfMessage *topmsg = field->message;
88 4 : printf("prefix: %d\n", (int)topmsg->prefix);
89 :
90 4 : if (field->tag == OneOfMessage_submsg1_tag)
91 : {
92 1 : SubMsg1 *msg = field->pData;
93 1 : printf("submsg1 {\n");
94 1 : msg->array.funcs.decode = print_int32;
95 1 : msg->array.arg = " array: %d\n";
96 : }
97 3 : else if (field->tag == OneOfMessage_submsg2_tag)
98 : {
99 1 : SubMsg2 *msg = field->pData;
100 1 : printf("submsg2 {\n");
101 1 : msg->strvalue.funcs.decode = print_string;
102 1 : msg->strvalue.arg = " strvalue: \"%s\"\n";
103 : }
104 2 : 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 2 : 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 4 : return true;
119 : }
120 :
121 6 : int main(int argc, char **argv)
122 : {
123 : uint8_t buffer[256];
124 6 : OneOfMessage msg = OneOfMessage_init_zero;
125 : pb_istream_t stream;
126 : size_t count;
127 :
128 : SET_BINARY_MODE(stdin);
129 6 : count = fread(buffer, 1, sizeof(buffer), stdin);
130 :
131 6 : if (!feof(stdin))
132 : {
133 0 : fprintf(stderr, "Message does not fit in buffer\n");
134 0 : 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 6 : msg.cb_values.funcs.decode = msg_callback;
140 6 : stream = pb_istream_from_buffer(buffer, count);
141 6 : if (!pb_decode(&stream, OneOfMessage_fields, &msg))
142 : {
143 0 : fprintf(stderr, "Decoding failed: %s\n", PB_GET_ERROR(&stream));
144 0 : return 1;
145 : }
146 :
147 : /* This is just printing for the test case logic */
148 6 : if (msg.which_values == OneOfMessage_intvalue_tag)
149 : {
150 1 : printf("prefix: %d\n", (int)msg.prefix);
151 1 : printf("intvalue: %d\n", (int)msg.values.intvalue);
152 : }
153 5 : else if (msg.which_values == OneOfMessage_strvalue_tag)
154 : {
155 1 : printf("prefix: %d\n", (int)msg.prefix);
156 1 : printf("strvalue: \"%s\"\n", msg.values.strvalue);
157 : }
158 4 : else if (msg.which_values == OneOfMessage_submsg3_tag &&
159 2 : msg.values.submsg3.which_values == SubMsg3_intvalue_tag)
160 : {
161 1 : printf(" intvalue: %d\n", (int)msg.values.submsg3.values.intvalue);
162 1 : printf("}\n");
163 : }
164 : else
165 : {
166 3 : printf("}\n");
167 : }
168 6 : printf("suffix: %d\n", (int)msg.suffix);
169 :
170 6 : assert(msg.prefix == 123);
171 6 : assert(msg.suffix == 321);
172 :
173 6 : return 0;
174 : }
|