884cf18e4addf092174c3efee9d0a5d7d705f1c4
[apps/agl-service-can-low-level.git] / pb_decode.c
1 /* pb_decode.c -- decode a protobuf using callback functions
2  *
3  * 2011 Petteri Aimonen <jpa@kapsi.fi>
4  */
5
6 #include "pb_decode.h"
7
8 const pb_decoder_t PB_DECODERS[PB_LAST_ACT] = {
9     NULL,
10     &pb_dec_uint32,
11     &pb_dec_sint32,
12     &pb_dec_uint32, // Cast to int32
13     &pb_dec_fixed32,
14     &pb_dec_fixed32, // Cast to int32
15     &pb_dec_uint64,
16     &pb_dec_sint64,
17     &pb_dec_uint64, // Cast to int64
18     &pb_dec_fixed64,
19     &pb_dec_fixed64, // Cast to int64
20     &pb_dec_bool,
21     &pb_dec_float,
22     &pb_dec_double,
23     &pb_dec_bytes,
24     &pb_dec_string,
25     &pb_dec_submessage
26 };
27
28 enum wire_type {
29     WT_VARINT = 0,
30     WT_64BIT  = 1,
31     WT_STRING = 2,
32     WT_32BIT  = 5
33 };
34
35 // Note: pb_decode_varint32 is a bit un-orthodox:
36 // it will refuse to decode values that exceed uint32 range.
37 // The Google implementation would simply cast to 32 bits.
38 bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
39 {
40     char byte;
41     int bitpos = 0;
42     *dest = 0;
43     
44     while (bitpos < 32 && pb_read(stream, &byte, 1))
45     {
46         *dest |= (byte & 0x7F) << bitpos;
47         bitpos += 7;
48         
49         if (!(byte & 0x80))
50             return true;
51     }
52     
53     return false;
54 }
55
56 bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest)
57 {
58     char byte;
59     int bitpos = 0;
60     *dest = 0;
61     
62     while (bitpos < 64 && pb_read(stream, &byte, 1))
63     {
64         *dest |= (byte & 0x7F) << bitpos;
65         bitpos += 7;
66         
67         if (!(byte & 0x80))
68             return true;
69     }
70     
71     return false;
72 }
73
74 bool pb_skip_varint(pb_istream_t *stream)
75 {
76     char byte;
77     do
78     {
79         if (!pb_read(stream, &byte, 1))
80             return false;
81     } while (byte & 0x80);
82     return true;
83 }
84
85 bool pb_skip_string(pb_istream_t *stream)
86 {
87     uint32_t length;
88     if (!pb_decode_varint32(stream, &length))
89         return false;
90     
91     return pb_read(stream, NULL, length);
92 }
93
94 bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest)
95 {
96     while (stream->bytes_left)
97     {
98         uint32_t temp;
99         if (!pb_decode_varint32(stream, &temp))
100             return false;
101         
102         int field_number = temp >> 3;
103         int wire_type = temp & 7;
104         
105         const pb_field_t *field = fields;
106         while (field->field_number != 0)
107         {
108             if (field->field_number != field_number)
109             {
110                 field++;
111                 continue;
112             }
113             
114             void *destfield = dest + field->offset; 
115             
116             if (field->action == PB_ACT_HAS)
117             {
118                 *(bool*)destfield = true;
119                 field++;
120                 continue;
121             }
122             
123             pb_decoder_t func = PB_DECODERS[field->action];
124             if (!func(stream, field, destfield))
125                 return false;
126             
127             break;
128         }
129         
130         if (field->field_number == 0) // No match found, skip data
131         {
132             bool status = false;
133             switch (wire_type)
134             {
135                 case WT_VARINT:
136                     status = pb_skip_varint(stream);
137                     break;
138                 
139                 case WT_64BIT:
140                     status = pb_read(stream, NULL, 8);
141                     break;
142                 
143                 case WT_STRING:
144                     status = pb_skip_string(stream);
145                     break;
146                 
147                 case WT_32BIT:
148                     status = pb_read(stream, NULL, 4);
149                     break;
150             }
151             
152             if (!status)
153                 return false;
154         }
155     }
156     
157     return true;
158 }
159
160 bool pb_dec_uint32(pb_istream_t *stream, const pb_field_t *field, void *dest)
161 {
162     return pb_decode_varint32(stream, (uint32_t*)dest);
163 }
164
165 bool pb_dec_sint32(pb_istream_t *stream, const pb_field_t *field, void *dest)
166 {
167     uint32_t *x = (uint32_t*)dest;
168     bool status = pb_decode_varint32(stream, x);
169     *x = (*x >> 1) ^ -(int32_t)(*x & 1);
170     return status;
171 }
172
173 bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
174 {
175     char bytes[4] = {0};
176     bool status = pb_read(stream, bytes, 4);
177     *(uint32_t*)dest = 
178         bytes[0] | ((uint32_t)bytes[1] << 8) |
179         ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24);
180     return status;
181 }
182
183 bool pb_dec_uint64(pb_istream_t *stream, const pb_field_t *field, void *dest)
184 {
185     return pb_decode_varint64(stream, (uint64_t*)dest);
186 }
187
188 bool pb_dec_sint64(pb_istream_t *stream, const pb_field_t *field, void *dest)
189 {
190     uint64_t *x = (uint64_t*)dest;
191     bool status = pb_decode_varint64(stream, x);
192     *x = (*x >> 1) ^ -(int64_t)(*x & 1);
193     return status;
194 }
195
196 bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
197 {
198     char bytes[8] = {0};
199     bool status = pb_read(stream, bytes, 8);
200     *(uint64_t*)dest =
201         (uint64_t)bytes[0] | ((uint64_t)bytes[1] << 8) |
202         ((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) |
203         ((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) |
204         ((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56);
205     return status;
206 }
207
208 bool pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest)
209 {
210     uint32_t temp = 0;
211     bool status = pb_decode_varint32(stream, &temp);
212     *(bool*)dest = !!temp;
213     return status;
214 }
215
216 bool pb_dec_float(pb_istream_t *stream, const pb_field_t *field, void *dest)
217 {
218     return pb_read(stream, (char*)dest, sizeof(float));
219 }
220
221 bool pb_dec_double(pb_istream_t *stream, const pb_field_t *field, void *dest)
222 {
223     return pb_read(stream, (char*)dest, sizeof(double));
224 }
225
226 bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
227 {
228     pb_bytearray_t *x = (pb_bytearray_t*)dest;
229     uint32_t temp;
230     if (!pb_decode_varint32(stream, &temp))
231         return false;
232     x->size = temp;
233     
234     if (x->size > field->fieldsize)
235         return false;
236     
237     return pb_read(stream, x->bytes, x->size);
238 }
239
240 bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
241 {
242     uint32_t size;
243     if (!pb_decode_varint32(stream, &size))
244         return false;
245     
246     if (size > field->fieldsize - 1)
247         return false;
248     
249     bool status = pb_read(stream, (char*)dest, size);
250     *((char*)dest + size) = 0;
251     return status;
252 }
253
254 bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
255 {
256     pb_callback_t *x = (pb_callback_t*)dest;
257     
258     if (x->funcs.decode == NULL)
259         return pb_skip_string(stream);
260     
261     uint32_t size;
262     if (!pb_decode_varint32(stream, &size))
263         return false;
264     
265     if (stream->bytes_left < size)
266         return false;
267     
268     // Make a limited-length istream for decoding submessage
269     pb_istream_t shortstream = *stream;
270     shortstream.bytes_left = size;
271     bool status = x->funcs.decode(&shortstream, field, x->arg);
272     stream->bytes_left -= size - shortstream.bytes_left;
273     return status;
274 }