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