Improvements, array support

git-svn-id: https://svn.kapsi.fi/jpa/nanopb@943 e3a754e5-d11d-0410-8d38-ebb782a927b9
This commit is contained in:
Petteri Aimonen
2011-07-27 19:22:11 +00:00
parent 14bbe22997
commit 84304b343a
4 changed files with 457 additions and 196 deletions

162
pb.h
View File

@@ -3,19 +3,36 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> // size_t #include <stddef.h> // size_t
#include <stdbool.h>
#ifdef __GNUC__
// This just reduces memory requirements, but is not required.
#define pb_packed __attribute__((packed))
#else
#define pb_packed
#endif
/* Lightweight input stream. /* Lightweight input stream.
* If buf is NULL, read but don't store bytes. */ * If buf is NULL, read but don't store bytes.
* You have to provide a callback function for reading.
* You can use state to store your own data (e.g. buffer pointer),
* and rely on pb_read to verify that no-body reads past bytes_left.
* However, substreams may change bytes_left so don't use that to
* compute any pointers.
*/
typedef struct _pb_istream_t pb_istream_t; typedef struct _pb_istream_t pb_istream_t;
struct _pb_istream_t struct _pb_istream_t
{ {
bool (*callback)(pb_istream_t *stream, char *buf, size_t count); bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count);
void *state; // Free field for use by callback implementation void *state; // Free field for use by callback implementation
size_t bytes_left; size_t bytes_left;
}; };
static inline bool pb_read(pb_istream_t *stream, char *buf, size_t count) static inline bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
{ {
if (stream->bytes_left < count)
return false;
bool status = stream->callback(stream, buf, count); bool status = stream->callback(stream, buf, count);
stream->bytes_left -= count; stream->bytes_left -= count;
return status; return status;
@@ -25,74 +42,129 @@ static inline bool pb_read(pb_istream_t *stream, char *buf, size_t count)
typedef struct _pb_ostream_t pb_ostream_t; typedef struct _pb_ostream_t pb_ostream_t;
struct _pb_ostream_t struct _pb_ostream_t
{ {
bool (*callback)(pb_ostream_t *stream, const char *buf, size_t count); bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count);
void *state; // Free field for use by callback implementation void *state; // Free field for use by callback implementation
size_t bytes_written; size_t bytes_written;
}; };
static inline bool pb_write(pb_ostream_t *stream, const char *buf, size_t count) static inline bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{ {
bool status = stream->callback(stream, buf, count); bool status = stream->callback(stream, buf, count);
stream->bytes_written += count; stream->bytes_written += count;
return status; return status;
} }
/* List of possible decode/encode action types */ /* List of possible field types
* Least-significant 4 bits tell the scalar type
* Most-significant 4 bits specify repeated/required/packed etc.
*
* INT32 and UINT32 are treated the same, as are (U)INT64 and (S)FIXED*
* These types are simply casted to correct field type when they are
* assigned to the memory pointer.
* SINT* is different, though, because it is zig-zag coded.
*/
typedef enum { typedef enum {
// Special case. Sets boolean field to true, continues parsing the data.
PB_ACT_HAS,
// Standard integer types // Standard integer types
PB_ACT_UINT32, PB_LTYPE_UINT32 = 0x00,
PB_ACT_SINT32, PB_LTYPE_INT32 = 0x00,
PB_ACT_INT32, PB_LTYPE_SINT32 = 0x01,
PB_ACT_FIXED32, PB_LTYPE_FIXED32 = 0x02,
PB_ACT_SFIXED32, PB_LTYPE_SFIXED32 = 0x02,
PB_ACT_UINT64, PB_LTYPE_UINT64 = 0x03,
PB_ACT_SINT64, PB_LTYPE_INT64 = 0x03,
PB_ACT_INT64, PB_LTYPE_SINT64 = 0x04,
PB_ACT_FIXED64, PB_LTYPE_FIXED64 = 0x05,
PB_ACT_SFIXED64, PB_LTYPE_SFIXED64 = 0x05,
PB_ACT_BOOL, PB_LTYPE_BOOL = 0x06,
PB_LTYPE_ENUM = 0x07,
// Standard float types // Standard float types
PB_ACT_FLOAT, PB_LTYPE_FLOAT = 0x08,
PB_ACT_DOUBLE, PB_LTYPE_DOUBLE = 0x09,
// Constant-sized array // Byte array with pre-allocated buffer.
PB_ACT_BYTES, // data_size is the length of the allocated PB_BYTES_ARRAY structure.
PB_LTYPE_BYTES = 0x0A,
// Constant-sized array, with null termination // String with pre-allocated buffer.
PB_ACT_STRING, // data_size is the maximum length.
PB_LTYPE_STRING = 0x0B,
// Callback function pointer in field value // Submessage
PB_ACT_SUBMESSAGE, // submsg_fields is pointer to field descriptions
PB_LTYPE_SUBMESSAGE = 0x0C,
PB_LAST_ACT /////////////
} pb_action_t; // Modifier flags
// Just the basic, write data at data_offset
PB_HTYPE_REQUIRED = 0x00,
// Write true at size_offset
PB_HTYPE_OPTIONAL = 0x10,
// Read to pre-allocated array
// Maximum number of entries is array_size,
// actual number is stored at size_offset
PB_HTYPE_ARRAY = 0x20,
// Works for all required/optional/repeated fields.
// data_offset points to pb_callback_t structure.
// LTYPE is ignored.
PB_HTYPE_CALLBACK = 0x30
} pb_packed pb_type_t;
// This structure is used in constants to specify struct fields. #define PB_HTYPE(x) ((x) & 0xF0)
typedef struct { #define PB_LTYPE(x) ((x) & 0x0F)
int field_number;
uint16_t offset; /* This structure is used in auto-generated constants
pb_action_t action; * to specify struct fields.
uint8_t fieldsize; * You can change field sizes here if you need structures
} pb_field_t; * larger than 256 bytes or field tags larger than 256.
* The compiler should complain if your .proto has such
* structures ("initializer too large for type").
*/
typedef struct _pb_field_t pb_field_t;
struct _pb_field_t {
uint8_t tag;
pb_type_t type;
uint8_t data_offset; // Offset of actual data or array start
uint8_t size_offset; // Offset of array size or has-boolean
uint8_t data_size; // Data size in bytes for a single item
uint8_t array_size; // Maximum number of entries in array
// Field definitions for submessage
// OR default value for all other non-array, non-callback types
// If null, then field will zeroed.
const void *ptr;
} pb_packed;
#define PB_LAST_FIELD {0,0,0,0} #define PB_LAST_FIELD {0,0,0,0}
/* --- Types to use inside generated structures. --- */ // This structure is used for 'bytes' arrays.
// It has the number of bytes in the beginning, and after that an array.
#define PB_BYTES_ARRAY(buffersize) \
struct { \
size_t size; \
uint8_t bytes[buffersize]; \
}
// Byte array and size word. typedef PB_BYTES_ARRAY(1) pb_bytes_array_t;
// Note: because of variable length array, this type cannot be directly used.
// Autogenerated code declares the same type of fields but with explicit length.
typedef struct {
size_t size;
char bytes[];
} pb_bytearray_t;
// This structure is used for giving the callback function. // This structure is used for giving the callback function.
//
// The decoding callback will be given a limited-length stream
// If the wire type was string, the length is the length of the string.
// If the wire type was a varint/fixed32/fixed64, the length is the length
// of the actual value.
// The function may be called multiple times (especially for repeated types,
// but also otherwise if the message happens to contain the field multiple
// times.)
//
// The encoding callback will receive the actual output stream.
// It should write all the data in one call, including the field tag and
// wire type. It can write multiple fields.
typedef struct _pb_callback_t pb_callback_t; typedef struct _pb_callback_t pb_callback_t;
struct _pb_callback_t { struct _pb_callback_t {
union { union {

View File

@@ -1,61 +1,63 @@
/* pb_decode.c -- decode a protobuf using callback functions /* pb_decode.c -- decode a protobuf using minimal resources
* *
* 2011 Petteri Aimonen <jpa@kapsi.fi> * 2011 Petteri Aimonen <jpa@kapsi.fi>
*/ */
#include "pb.h"
#include "pb_decode.h" #include "pb_decode.h"
#include <string.h>
const pb_decoder_t PB_DECODERS[PB_LAST_ACT] = { const pb_decoder_t PB_DECODERS[16] = {
NULL,
&pb_dec_uint32, &pb_dec_uint32,
&pb_dec_sint32, &pb_dec_sint32,
&pb_dec_uint32, // Cast to int32
&pb_dec_fixed32, &pb_dec_fixed32,
&pb_dec_fixed32, // Cast to int32
&pb_dec_uint64, &pb_dec_uint64,
&pb_dec_sint64, &pb_dec_sint64,
&pb_dec_uint64, // Cast to int64
&pb_dec_fixed64, &pb_dec_fixed64,
&pb_dec_fixed64, // Cast to int64
&pb_dec_bool, &pb_dec_bool,
&pb_dec_enum,
&pb_dec_float, &pb_dec_float,
&pb_dec_double, &pb_dec_double,
&pb_dec_bytes, &pb_dec_bytes,
&pb_dec_string, &pb_dec_string,
&pb_dec_submessage &pb_dec_submessage
}; };
enum wire_type { ////////////////////////
WT_VARINT = 0, // Helper functions
WT_64BIT = 1, ////////////////////////
WT_STRING = 2,
WT_32BIT = 5 static bool buf_read(pb_istream_t *stream, uint8_t *buf, size_t count)
}; {
uint8_t *source = (uint8_t*)stream->state;
if (buf != NULL)
memcpy(buf, source, count);
stream->state = source + count;
return true;
}
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize)
{
pb_istream_t stream = {&buf_read, buf, bufsize};
return stream;
}
// Note: pb_decode_varint32 is a bit un-orthodox:
// it will refuse to decode values that exceed uint32 range.
// The Google implementation would simply cast to 32 bits.
bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
{ {
char byte; uint64_t temp;
int bitpos = 0; if (!pb_decode_varint64(stream, &temp))
*dest = 0; return false;
*dest = temp;
while (bitpos < 32 && pb_read(stream, &byte, 1)) return true;
{
*dest |= (byte & 0x7F) << bitpos;
bitpos += 7;
if (!(byte & 0x80))
return true;
}
return false;
} }
bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest) bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest)
{ {
char byte; uint8_t byte;
int bitpos = 0; int bitpos = 0;
*dest = 0; *dest = 0;
@@ -73,7 +75,7 @@ bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest)
bool pb_skip_varint(pb_istream_t *stream) bool pb_skip_varint(pb_istream_t *stream)
{ {
char byte; uint8_t byte;
do do
{ {
if (!pb_read(stream, &byte, 1)) if (!pb_read(stream, &byte, 1))
@@ -91,72 +93,235 @@ bool pb_skip_string(pb_istream_t *stream)
return pb_read(stream, NULL, length); return pb_read(stream, NULL, length);
} }
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest) /* Currently all wire type related stuff is kept hidden from
* callbacks. They shouldn't need it. It's better for performance
* to just assume the correct type and fail safely on corrupt message.
*/
enum wire_type_t {
WT_VARINT = 0,
WT_64BIT = 1,
WT_STRING = 2,
WT_32BIT = 5
};
static bool skip(pb_istream_t *stream, int wire_type)
{ {
switch (wire_type)
{
case WT_VARINT: return pb_skip_varint(stream);
case WT_64BIT: return pb_read(stream, NULL, 8);
case WT_STRING: return pb_skip_string(stream);
case WT_32BIT: return pb_read(stream, NULL, 4);
default: return false;
}
}
// Read a raw value to buffer, for the purpose of passing it to callback.
// Size is maximum size on call, and actual size on return.
static bool read_raw_value(pb_istream_t *stream, int wire_type, uint8_t *buf, size_t *size)
{
size_t max_size = *size;
switch (wire_type)
{
case WT_VARINT:
*size = 0;
do
{
(*size)++;
if (*size > max_size) return false;
if (!pb_read(stream, buf++, 1)) return false;
} while (*buf & 0x80);
return true;
case WT_64BIT:
*size = 8;
return pb_read(stream, buf, 8);
case WT_32BIT:
*size = 4;
return pb_read(stream, buf, 4);
default: return false;
}
}
// Decode string length from stream and return a substream with limited length
static bool make_string_substream(pb_istream_t *stream, pb_istream_t *substream)
{
uint32_t size;
if (!pb_decode_varint32(stream, &size))
return false;
*substream = *stream;
if (substream->bytes_left < size)
return false;
substream->bytes_left = size;
stream->bytes_left -= size;
return true;
}
bool decode_field(pb_istream_t *stream, int wire_type, const pb_field_t *field, void *dest_struct)
{
pb_decoder_t func = PB_DECODERS[PB_LTYPE(field->type)];
void *pData = (char*)dest_struct + field->data_offset;
void *pSize = (char*)dest_struct + field->size_offset;
switch (PB_HTYPE(field->type))
{
case PB_HTYPE_REQUIRED:
return func(stream, field, pData);
case PB_HTYPE_OPTIONAL:
*(bool*)pSize = true;
return func(stream, field, pData);
case PB_HTYPE_ARRAY:
if (wire_type == WT_STRING
&& PB_LTYPE(field->type) != PB_LTYPE_BYTES
&& PB_LTYPE(field->type) != PB_LTYPE_STRING
&& PB_LTYPE(field->type) != PB_LTYPE_SUBMESSAGE)
{
// Packed array
size_t *size = (size_t*)pSize;
pb_istream_t substream;
if (!make_string_substream(stream, &substream))
return false;
while (substream.bytes_left && *size < field->array_size)
{
void *pItem = pData + field->data_size * (*size);
if (!func(stream, field, pItem))
return false;
(*size)++;
}
return (substream.bytes_left == 0);
}
else
{
// Repeated field
size_t *size = (size_t*)pSize;
if (*size >= field->array_size)
return false;
void *pItem = pData + field->data_size * (*size);
(*size)++;
return func(stream, field, pItem);
}
case PB_HTYPE_CALLBACK:
if (wire_type == WT_STRING)
{
pb_callback_t *pCallback = (pb_callback_t*)pData;
pb_istream_t substream;
if (!make_string_substream(stream, &substream))
return false;
while (substream.bytes_left)
{
if (!pCallback->funcs.decode(&substream, field, pCallback->arg))
return false;
}
}
else
{
// Copy the single scalar value to stack.
// This is required so that we can limit the stream length,
// which in turn allows to use same callback for packed and
// not-packed fields.
uint8_t buffer[10];
size_t size = sizeof(buffer);
if (!read_raw_value(stream, wire_type, buffer, &size))
return false;
pb_istream_t substream = pb_istream_from_buffer(buffer, size);
pb_callback_t *pCallback = (pb_callback_t*)pData;
return pCallback->funcs.decode(&substream, field, pCallback->arg);
}
default:
return false;
}
}
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
{
// Used to check for required fields
uint32_t fields_seen = 0;
int i;
// Initialize size/has fields and apply default values
for (i = 0; fields[i].tag != 0; i++)
{
void *pData = (char*)dest_struct + fields[i].data_offset;
void *pSize = (char*)dest_struct + fields[i].size_offset;
if (PB_HTYPE(fields[i].type) == PB_HTYPE_OPTIONAL)
{
*(bool*)pSize = false;
}
else if (PB_HTYPE(fields[i].type) == PB_HTYPE_ARRAY)
{
*(size_t*)pSize = 0;
}
if (PB_HTYPE(fields[i].type) != PB_HTYPE_ARRAY &&
PB_HTYPE(fields[i].type) != PB_HTYPE_CALLBACK)
{
if (fields[i].ptr != NULL)
{
memcpy(pData, fields[i].ptr, fields[i].data_size);
}
else
{
memset(pData, 0, fields[i].data_size);
}
}
}
while (stream->bytes_left) while (stream->bytes_left)
{ {
uint32_t temp; uint32_t temp;
if (!pb_decode_varint32(stream, &temp)) if (!pb_decode_varint32(stream, &temp))
return false; return false;
int field_number = temp >> 3; int tag = temp >> 3;
int wire_type = temp & 7; int wire_type = temp & 7;
const pb_field_t *field = fields; i = 0;
while (field->field_number != 0) while (fields[i].tag != 0 && fields[i].tag != tag)
{ {
if (field->field_number != field_number) i++;
{
field++;
continue;
}
void *destfield = dest + field->offset;
if (field->action == PB_ACT_HAS)
{
*(bool*)destfield = true;
field++;
continue;
}
pb_decoder_t func = PB_DECODERS[field->action];
if (!func(stream, field, destfield))
return false;
break;
} }
if (field->field_number == 0) // No match found, skip data if (fields[i].tag == 0) // No match found, skip data
{ {
bool status = false; skip(stream, wire_type);
switch (wire_type) continue;
{ }
case WT_VARINT:
status = pb_skip_varint(stream); fields_seen |= 1 << (i & 31);
break;
case WT_64BIT:
status = pb_read(stream, NULL, 8);
break;
case WT_STRING:
status = pb_skip_string(stream);
break;
case WT_32BIT:
status = pb_read(stream, NULL, 4);
break;
}
if (!status) if (!decode_field(stream, wire_type, &fields[i], dest_struct))
return false; return false;
}
// Check that all required fields (mod 31) were present.
for (i = 0; fields[i].tag != 0; i++)
{
if (PB_HTYPE(fields[i].type) == PB_HTYPE_REQUIRED &&
!(fields_seen & (1 << (i & 31))))
{
return false;
} }
} }
return true; return true;
} }
/* Field decoders */
bool pb_dec_uint32(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_dec_uint32(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
return pb_decode_varint32(stream, (uint32_t*)dest); return pb_decode_varint32(stream, (uint32_t*)dest);
@@ -172,11 +337,15 @@ bool pb_dec_sint32(pb_istream_t *stream, const pb_field_t *field, void *dest)
bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
char bytes[4] = {0}; uint8_t bytes[4] = {0};
bool status = pb_read(stream, bytes, 4); bool status = pb_read(stream, bytes, 4);
*(uint32_t*)dest =
bytes[0] | ((uint32_t)bytes[1] << 8) | #ifdef __BIG_ENDIAN__
((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); uint8_t lebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
memcpy(dest, lebytes, 4);
#else
memcpy(dest, bytes, 4);
#endif
return status; return status;
} }
@@ -195,13 +364,16 @@ bool pb_dec_sint64(pb_istream_t *stream, const pb_field_t *field, void *dest)
bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
char bytes[8] = {0}; uint8_t bytes[8] = {0};
bool status = pb_read(stream, bytes, 8); bool status = pb_read(stream, bytes, 8);
*(uint64_t*)dest =
(uint64_t)bytes[0] | ((uint64_t)bytes[1] << 8) | #ifdef __BIG_ENDIAN__
((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) | uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4],
((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) | bytes[3], bytes[2], bytes[1], bytes[0]};
((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56); memcpy(dest, lebytes, 4);
#else
memcpy(dest, bytes, 4);
#endif
return status; return status;
} }
@@ -213,25 +385,37 @@ bool pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest)
return status; return status;
} }
bool pb_dec_enum(pb_istream_t *stream, const pb_field_t *field, void *dest)
{
// Enum sizes can vary, copy only data_size amount of bytes.
uint32_t temp = 0;
bool status = pb_decode_varint32(stream, &temp);
memcpy(dest, &temp, field->data_size);
return status;
}
bool pb_dec_float(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_dec_float(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
return pb_read(stream, (char*)dest, sizeof(float)); return pb_read(stream, (uint8_t*)dest, sizeof(float));
} }
bool pb_dec_double(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_dec_double(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
return pb_read(stream, (char*)dest, sizeof(double)); return pb_read(stream, (uint8_t*)dest, sizeof(double));
} }
bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
pb_bytearray_t *x = (pb_bytearray_t*)dest; pb_bytes_array_t *x = (pb_bytes_array_t*)dest;
uint32_t temp; uint32_t temp;
if (!pb_decode_varint32(stream, &temp)) if (!pb_decode_varint32(stream, &temp))
return false; return false;
x->size = temp; x->size = temp;
if (x->size > field->fieldsize) // Note: data_size includes the size of the x.size field, too.
// Calculate actual size starting from offset.
if (x->size > field->data_size - offsetof(pb_bytes_array_t, bytes))
return false; return false;
return pb_read(stream, x->bytes, x->size); return pb_read(stream, x->bytes, x->size);
@@ -243,32 +427,23 @@ bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
if (!pb_decode_varint32(stream, &size)) if (!pb_decode_varint32(stream, &size))
return false; return false;
if (size > field->fieldsize - 1) if (size > field->data_size - 1)
return false; return false;
bool status = pb_read(stream, (char*)dest, size); bool status = pb_read(stream, (uint8_t*)dest, size);
*((char*)dest + size) = 0; *((uint8_t*)dest + size) = 0;
return status; return status;
} }
bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest) bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
pb_callback_t *x = (pb_callback_t*)dest; pb_istream_t substream;
if (x->funcs.decode == NULL) if (!make_string_substream(stream, &substream))
return pb_skip_string(stream);
uint32_t size;
if (!pb_decode_varint32(stream, &size))
return false; return false;
if (stream->bytes_left < size) if (field->ptr == NULL)
return false; return false;
// Make a limited-length istream for decoding submessage return pb_decode(&substream, (pb_field_t*)field->ptr, dest);
pb_istream_t shortstream = *stream;
shortstream.bytes_left = size;
bool status = x->funcs.decode(&shortstream, field, x->arg);
stream->bytes_left -= size - shortstream.bytes_left;
return status;
} }

View File

@@ -6,12 +6,14 @@
// Decode from stream to destination struct. // Decode from stream to destination struct.
// The actual struct pointed to by dest must match the description in fields. // The actual struct pointed to by dest must match the description in fields.
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest); bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
/* --- Helper functions --- /* --- Helper functions ---
* You may want to use these from your callbacks. * You may want to use these from your caller or callbacks.
*/ */
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize);
bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest); bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest);
@@ -20,10 +22,10 @@ bool pb_skip_string(pb_istream_t *stream);
/* --- Field decoders --- /* --- Field decoders ---
* Each decoder takes stream and field description, and a pointer to the field * Each decoder takes stream and field description, and a pointer to the field
* in the destination struct (dest = struct_addr + field->offset). * in the destination struct (dest = struct_addr + field->data_offset).
* For arrays, these functions are called repeatedly.
*/ */
// Integer types.
bool pb_dec_uint32(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_uint32(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_sint32(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_sint32(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
@@ -31,25 +33,20 @@ bool pb_dec_uint64(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_sint64(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_sint64(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_enum(pb_istream_t *stream, const pb_field_t *field, void *dest);
// Floating point types. Info is ignored.
bool pb_dec_float(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_float(pb_istream_t *stream, const pb_field_t *field, void *dest);
bool pb_dec_double(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_double(pb_istream_t *stream, const pb_field_t *field, void *dest);
// Byte array. Dest is pointer to
bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
// Null-terminated string.
bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
// Use callback. Dest is pointer to pb_callback_t struct.
bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest); bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest); typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest);
/* --- Function pointers to field decoders --- /* --- Function pointers to field decoders ---
* Order in the array must match pb_action_t numbering. * Order in the array must match pb_action_t LTYPE numbering.
*/ */
const pb_decoder_t PB_DECODERS[PB_LAST_ACT]; const pb_decoder_t PB_DECODERS[16];
#endif #endif

View File

@@ -9,8 +9,6 @@ typedef enum {
Person_PhoneType_MOBILE = 0, Person_PhoneType_MOBILE = 0,
Person_PhoneType_HOME = 1, Person_PhoneType_HOME = 1,
Person_PhoneType_WORK = 2, Person_PhoneType_WORK = 2,
_Person_PhoneType_size = 0xFFFFFFFF // Force 32-bit enum
} Person_PhoneType; } Person_PhoneType;
typedef struct { typedef struct {
@@ -24,52 +22,71 @@ typedef struct {
int32_t id; int32_t id;
bool has_email; bool has_email;
char email[40]; char email[40];
size_t phone_size;
pb_callback_t phone; Person_PhoneNumber phone[5];
} Person; } Person;
/* Field descriptions */ /* Field descriptions */
#define membersize(st, m) (sizeof ((st*)0)->m) #define membersize(st, m) (sizeof ((st*)0)->m)
const Person_PhoneType Person_PhoneType_type_default = Person_PhoneType_HOME;
const pb_field_t Person_PhoneNumber_fields[] = { const pb_field_t Person_PhoneNumber_fields[] = {
{1, offsetof(Person_PhoneNumber, number), PB_ACT_STRING, membersize(Person_PhoneNumber, number)}, {1, PB_HTYPE_REQUIRED | PB_LTYPE_STRING,
{2, offsetof(Person_PhoneNumber, has_type), PB_ACT_HAS, membersize(Person_PhoneNumber, has_type)}, offsetof(Person_PhoneNumber, number), 0,
{2, offsetof(Person_PhoneNumber, type), PB_ACT_UINT32, membersize(Person_PhoneNumber, type)}, membersize(Person_PhoneNumber, number), 0, 0},
{2, PB_HTYPE_OPTIONAL | PB_LTYPE_ENUM,
offsetof(Person_PhoneNumber, type),
offsetof(Person_PhoneNumber, has_type),
membersize(Person_PhoneNumber, type), 0,
&Person_PhoneType_type_default},
PB_LAST_FIELD PB_LAST_FIELD
}; };
const pb_field_t Person_fields[] = { const pb_field_t Person_fields[] = {
{1, offsetof(Person, name), PB_ACT_STRING, membersize(Person, name)}, {1, PB_HTYPE_REQUIRED | PB_LTYPE_STRING,
{2, offsetof(Person, id), PB_ACT_INT32, membersize(Person, id)}, offsetof(Person, name), 0,
{3, offsetof(Person, email), PB_ACT_STRING, membersize(Person, email)}, membersize(Person, name), 0, 0},
{4, offsetof(Person, phone), PB_ACT_SUBMESSAGE, membersize(Person, phone)}
{2, PB_HTYPE_REQUIRED | PB_LTYPE_INT32,
offsetof(Person, id), 0,
membersize(Person, id), 0, 0},
{3, PB_HTYPE_OPTIONAL | PB_LTYPE_STRING,
offsetof(Person, email),
offsetof(Person, has_email),
membersize(Person, email), 0, 0},
{4, PB_HTYPE_ARRAY | PB_LTYPE_SUBMESSAGE,
offsetof(Person, phone),
offsetof(Person, phone_size),
membersize(Person, phone[0]),
membersize(Person, phone) / membersize(Person, phone[0]),
Person_PhoneNumber_fields},
PB_LAST_FIELD
}; };
/* Default value descriptions */
#define Person_PhoneNumber_default {"", false, Person_PhoneType_HOME};
#define Person_default {"", 0, false, "", {{0},0}};
/* And now, the actual test program */ /* And now, the actual test program */
bool print_phonenumber(pb_istream_t *stream, const pb_field_t *field, void *arg)
{
Person_PhoneNumber x = Person_PhoneNumber_default;
if (!pb_decode(stream, Person_PhoneNumber_fields, &x))
return false;
printf("PhoneNumber: number '%s' type '%d'\n", x.number, x.type);
return true;
}
bool print_person(pb_istream_t *stream) bool print_person(pb_istream_t *stream)
{ {
Person x = Person_default; int i;
x.phone.funcs.decode = &print_phonenumber; Person person;
if (!pb_decode(stream, Person_fields, &x)) if (!pb_decode(stream, Person_fields, &person))
return false; return false;
printf("Person: name '%s' id '%d' email '%s'\n", x.name, x.id, x.email); printf("Person: name '%s' id '%d' email '%s'\n", person.name, person.id, person.email);
for (i = 0; i < person.phone_size; i++)
{
Person_PhoneNumber *phone = &person.phone[i];
printf("PhoneNumber: number '%s' type '%d'\n", phone->number, phone->type);
}
return true; return true;
} }
@@ -91,10 +108,10 @@ bool my_read(pb_istream_t *stream, char *buf, size_t count)
int main() int main()
{ {
char buffer[512]; uint8_t buffer[512];
size_t size = fread(buffer, 1, 512, stdin); size_t size = fread(buffer, 1, 512, stdin);
pb_istream_t stream = {&my_read, buffer, size}; pb_istream_t stream = pb_istream_from_buffer(buffer, size);
if (!print_person(&stream)) if (!print_person(&stream))
printf("Parsing failed.\n"); printf("Parsing failed.\n");