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