check return values
[apps/low-level-can-service.git] / pb_encode.c
1 /* pb_encode.c -- encode a protobuf using minimal resources
2  *
3  * 2011 Petteri Aimonen <jpa@kapsi.fi>
4  */
5
6 #include "pb.h"
7 #include "pb_encode.h"
8 #include <string.h>
9
10 #ifdef __GNUC__
11 /* Verify that we remember to check all return values for proper error propagation */
12 #define checkreturn __attribute__((warn_unused_result))
13 #else
14 #define checkreturn
15 #endif
16
17
18 typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
19
20 /* --- Function pointers to field encoders ---
21  * Order in the array must match pb_action_t LTYPE numbering.
22  */
23 static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
24     &pb_enc_varint,
25     &pb_enc_svarint,
26     &pb_enc_fixed,
27     
28     &pb_enc_bytes,
29     &pb_enc_string,
30     &pb_enc_submessage
31 };
32
33 /* pb_ostream_t implementation */
34
35 static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
36 {
37     uint8_t *dest = (uint8_t*)stream->state;
38     memcpy(dest, buf, count);
39     stream->state = dest + count;
40     return true;
41 }
42
43 pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize)
44 {
45     pb_ostream_t stream;
46     stream.callback = &buf_write;
47     stream.state = buf;
48     stream.max_size = bufsize;
49     stream.bytes_written = 0;
50     return stream;
51 }
52
53 bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
54 {
55     if (stream->callback != NULL)
56     {
57         if (stream->bytes_written + count > stream->max_size)
58             return false;
59         
60         if (!stream->callback(stream, buf, count))
61             return false;
62     }
63     
64     stream->bytes_written += count;
65     return true;
66 }
67
68 /* Main encoding stuff */
69
70 static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
71                          const void *pData, size_t count, pb_encoder_t func)
72 {
73     int i;
74     const void *p;
75     size_t size;
76     
77     if (PB_LTYPE(field->type) < PB_LTYPE_LAST_PACKABLE)
78     {
79         if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
80             return false;
81         
82         /* Determine the total size of packed array. */
83         if (PB_LTYPE(field->type) == PB_LTYPE_FIXED)
84         {
85             size = field->data_size * count;
86         }
87         else
88         {
89             pb_ostream_t sizestream = {0};
90             p = pData;
91             for (i = 0; i < count; i++)
92             {
93                 if (!func(&sizestream, field, p))
94                     return false;
95                 p = (const char*)p + field->data_size;
96             }
97             size = sizestream.bytes_written;
98         }
99         
100         if (!pb_encode_varint(stream, size))
101             return false;
102         
103         if (stream->callback == NULL)
104             return pb_write(stream, NULL, size); /* Just sizing.. */
105         
106         /* Write the data */
107         p = pData;
108         for (i = 0; i < count; i++)
109         {
110             if (!func(stream, field, p))
111                 return false;
112             p = (const char*)p + field->data_size;
113         }
114     }
115     else
116     {
117         p = pData;
118         for (i = 0; i < count; i++)
119         {
120             if (!pb_encode_tag_for_field(stream, field))
121                 return false;
122             if (!func(stream, field, p))
123                 return false;
124             p = (const char*)p + field->data_size;
125         }
126     }
127     
128     return true;
129 }
130
131 bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
132 {
133     const pb_field_t *field = fields;
134     const void *pData = src_struct;
135     const void *pSize;
136     size_t prev_size = 0;
137     
138     while (field->tag != 0)
139     {
140         pData = (const char*)pData + prev_size + field->data_offset;
141         pSize = (const char*)pData + field->size_offset;
142         
143         prev_size = field->data_size;
144         if (PB_HTYPE(field->type) == PB_HTYPE_ARRAY)
145             prev_size *= field->array_size;
146         
147         pb_encoder_t func = PB_ENCODERS[PB_LTYPE(field->type)];
148         
149         switch (PB_HTYPE(field->type))
150         {
151             case PB_HTYPE_REQUIRED:
152                 if (!pb_encode_tag_for_field(stream, field))
153                     return false;
154                 if (!func(stream, field, pData))
155                     return false;
156                 break;
157             
158             case PB_HTYPE_OPTIONAL:
159                 if (*(bool*)pSize)
160                 {
161                     if (!pb_encode_tag_for_field(stream, field))
162                         return false;
163                 
164                     if (!func(stream, field, pData))
165                         return false;
166                 }
167                 break;
168             
169             case PB_HTYPE_ARRAY:
170                 if (!encode_array(stream, field, pData, *(size_t*)pSize, func))
171                     return false;
172                 break;
173             
174             case PB_HTYPE_CALLBACK:
175             {
176                 pb_callback_t *callback = (pb_callback_t*)pData;
177                 if (callback->funcs.encode != NULL)
178                 {
179                     if (!callback->funcs.encode(stream, field, callback->arg))
180                         return false;
181                 }
182                 break;
183             }
184         }
185     
186         field++;
187     }
188     
189     return true;
190 }
191
192 /* Helper functions */
193 bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
194 {
195     uint8_t buffer[10];
196     int i = 0;
197     
198     if (value == 0)
199         return pb_write(stream, (uint8_t*)&value, 1);
200     
201     while (value)
202     {
203         buffer[i] = (value & 0x7F) | 0x80;
204         value >>= 7;
205         i++;
206     }
207     buffer[i-1] &= 0x7F; /* Unset top bit on last byte */
208     
209     return pb_write(stream, buffer, i);
210 }
211
212 bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number)
213 {
214     int tag = wiretype | (field_number << 3);
215     return pb_encode_varint(stream, tag);
216 }
217
218 bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
219 {
220     pb_wire_type_t wiretype;
221     switch (PB_LTYPE(field->type))
222     {
223         case PB_LTYPE_VARINT:
224         case PB_LTYPE_SVARINT:
225             wiretype = PB_WT_VARINT;
226             break;
227         
228         case PB_LTYPE_FIXED:
229             if (field->data_size == 4)
230                 wiretype = PB_WT_32BIT;
231             else if (field->data_size == 8)
232                 wiretype = PB_WT_64BIT;
233             else
234                 return false;
235             break;
236         
237         case PB_LTYPE_BYTES:
238         case PB_LTYPE_STRING:
239         case PB_LTYPE_SUBMESSAGE:
240             wiretype = PB_WT_STRING;
241             break;
242         
243         default:
244             return false;
245     }
246     
247     return pb_encode_tag(stream, wiretype, field->tag);
248 }
249
250 bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size)
251 {
252     if (!pb_encode_varint(stream, size))
253         return false;
254     
255     return pb_write(stream, buffer, size);
256 }
257
258 /* Field encoders */
259
260 /* Copy srcsize bytes from src so that values are casted properly.
261  * On little endian machine, copy to start of dest
262  * On big endian machine, copy to end of dest
263  * destsize must always be larger than srcsize
264  * 
265  * Note: This is the reverse of the endian_copy in pb_decode.c.
266  */
267 static void endian_copy(void *dest, const void *src, size_t destsize, size_t srcsize)
268 {
269 #ifdef __BIG_ENDIAN__
270     memcpy((char*)dest + (destsize - srcsize), src, srcsize);
271 #else
272     memcpy(dest, src, srcsize);
273 #endif
274 }
275
276 bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
277 {
278     uint64_t value = 0;
279     endian_copy(&value, src, sizeof(value), field->data_size);
280     return pb_encode_varint(stream, value);
281 }
282
283 bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
284 {
285     uint64_t value = 0;
286     uint64_t zigzagged;
287     uint64_t signbitmask, xormask;
288     endian_copy(&value, src, sizeof(value), field->data_size);
289     
290     signbitmask = (uint64_t)0x80 << (field->data_size * 8 - 8);
291     xormask = ((uint64_t)-1) >> (64 - field->data_size * 8);
292     if (value & signbitmask)
293         zigzagged = ((value ^ xormask) << 1) | 1;
294     else
295         zigzagged = value << 1;
296     
297     return pb_encode_varint(stream, zigzagged);
298 }
299
300 bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_t *field, const void *src)
301 {
302     #ifdef __BIG_ENDIAN__
303     uint8_t bytes[8] = {0};
304     endian_copy(bytes, src, sizeof(bytes), field->data_size);
305     uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4], 
306                           bytes[3], bytes[2], bytes[1], bytes[0]};
307     return pb_write(stream, lebytes, field->data_size);
308     #else
309     return pb_write(stream, (uint8_t*)src, field->data_size);
310     #endif
311 }
312
313 bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
314 {
315     pb_bytes_array_t *bytes = (pb_bytes_array_t*)src;
316     return pb_encode_string(stream, bytes->bytes, bytes->size);
317 }
318
319 bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
320 {
321     return pb_encode_string(stream, (uint8_t*)src, strlen((char*)src));
322 }
323
324 bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
325 {
326     pb_ostream_t substream = {0};
327     size_t size;
328     bool status;
329     
330     if (field->ptr == NULL)
331         return false;
332     
333     if (!pb_encode(&substream, (pb_field_t*)field->ptr, src))
334         return false;
335     
336     size = substream.bytes_written;
337     
338     if (!pb_encode_varint(stream, size))
339         return false;
340     
341     if (stream->callback == NULL)
342         return pb_write(stream, NULL, size); /* Just sizing */
343     
344     if (stream->bytes_written + size > stream->max_size)
345         return false;
346         
347     /* Use a substream to verify that a callback doesn't write more than
348      * what it did the first time. */
349     substream.callback = stream->callback;
350     substream.state = stream->state;
351     substream.max_size = size;
352     substream.bytes_written = 0;
353     
354     status = pb_encode(&substream, (pb_field_t*)field->ptr, src);
355     
356     stream->bytes_written += substream.bytes_written;
357     
358     if (substream.bytes_written != size)
359         return false;
360     
361     return status;
362 }
363