1 /* pb_decode.c -- decode a protobuf using callback functions
3 * 2011 Petteri Aimonen <jpa@kapsi.fi>
8 const pb_decoder_t PB_DECODERS[PB_LAST_ACT] = {
12 &pb_dec_uint32, // Cast to int32
14 &pb_dec_fixed32, // Cast to int32
17 &pb_dec_uint64, // Cast to int64
19 &pb_dec_fixed64, // Cast to int64
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)
44 while (bitpos < 32 && pb_read(stream, &byte, 1))
46 *dest |= (byte & 0x7F) << bitpos;
56 bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest)
62 while (bitpos < 64 && pb_read(stream, &byte, 1))
64 *dest |= (byte & 0x7F) << bitpos;
74 bool pb_skip_varint(pb_istream_t *stream)
79 if (!pb_read(stream, &byte, 1))
81 } while (byte & 0x80);
85 bool pb_skip_string(pb_istream_t *stream)
88 if (!pb_decode_varint32(stream, &length))
91 return pb_read(stream, NULL, length);
94 bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest)
96 while (stream->bytes_left)
99 if (!pb_decode_varint32(stream, &temp))
102 int field_number = temp >> 3;
103 int wire_type = temp & 7;
105 const pb_field_t *field = fields;
106 while (field->field_number != 0)
108 if (field->field_number != field_number)
114 void *destfield = dest + field->offset;
116 if (field->action == PB_ACT_HAS)
118 *(bool*)destfield = true;
123 pb_decoder_t func = PB_DECODERS[field->action];
124 if (!func(stream, field, destfield))
130 if (field->field_number == 0) // No match found, skip data
136 status = pb_skip_varint(stream);
140 status = pb_read(stream, NULL, 8);
144 status = pb_skip_string(stream);
148 status = pb_read(stream, NULL, 4);
160 bool pb_dec_uint32(pb_istream_t *stream, const pb_field_t *field, void *dest)
162 return pb_decode_varint32(stream, (uint32_t*)dest);
165 bool pb_dec_sint32(pb_istream_t *stream, const pb_field_t *field, void *dest)
167 uint32_t *x = (uint32_t*)dest;
168 bool status = pb_decode_varint32(stream, x);
169 *x = (*x >> 1) ^ -(int32_t)(*x & 1);
173 bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
176 bool status = pb_read(stream, bytes, 4);
178 bytes[0] | ((uint32_t)bytes[1] << 8) |
179 ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24);
183 bool pb_dec_uint64(pb_istream_t *stream, const pb_field_t *field, void *dest)
185 return pb_decode_varint64(stream, (uint64_t*)dest);
188 bool pb_dec_sint64(pb_istream_t *stream, const pb_field_t *field, void *dest)
190 uint64_t *x = (uint64_t*)dest;
191 bool status = pb_decode_varint64(stream, x);
192 *x = (*x >> 1) ^ -(int64_t)(*x & 1);
196 bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
199 bool status = pb_read(stream, bytes, 8);
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);
208 bool pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest)
211 bool status = pb_decode_varint32(stream, &temp);
212 *(bool*)dest = !!temp;
216 bool pb_dec_float(pb_istream_t *stream, const pb_field_t *field, void *dest)
218 return pb_read(stream, (char*)dest, sizeof(float));
221 bool pb_dec_double(pb_istream_t *stream, const pb_field_t *field, void *dest)
223 return pb_read(stream, (char*)dest, sizeof(double));
226 bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
228 pb_bytearray_t *x = (pb_bytearray_t*)dest;
230 if (!pb_decode_varint32(stream, &temp))
234 if (x->size > field->fieldsize)
237 return pb_read(stream, x->bytes, x->size);
240 bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
243 if (!pb_decode_varint32(stream, &size))
246 if (size > field->fieldsize - 1)
249 bool status = pb_read(stream, (char*)dest, size);
250 *((char*)dest + size) = 0;
254 bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
256 pb_callback_t *x = (pb_callback_t*)dest;
258 if (x->funcs.decode == NULL)
259 return pb_skip_string(stream);
262 if (!pb_decode_varint32(stream, &size))
265 if (stream->bytes_left < size)
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;