Improvements, array support
git-svn-id: https://svn.kapsi.fi/jpa/nanopb@943 e3a754e5-d11d-0410-8d38-ebb782a927b9
This commit is contained in:
162
pb.h
162
pb.h
@@ -3,19 +3,36 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#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.
|
||||
* 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;
|
||||
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
|
||||
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);
|
||||
stream->bytes_left -= count;
|
||||
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;
|
||||
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
|
||||
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);
|
||||
stream->bytes_written += count;
|
||||
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 {
|
||||
// Special case. Sets boolean field to true, continues parsing the data.
|
||||
PB_ACT_HAS,
|
||||
|
||||
// Standard integer types
|
||||
PB_ACT_UINT32,
|
||||
PB_ACT_SINT32,
|
||||
PB_ACT_INT32,
|
||||
PB_ACT_FIXED32,
|
||||
PB_ACT_SFIXED32,
|
||||
PB_ACT_UINT64,
|
||||
PB_ACT_SINT64,
|
||||
PB_ACT_INT64,
|
||||
PB_ACT_FIXED64,
|
||||
PB_ACT_SFIXED64,
|
||||
PB_ACT_BOOL,
|
||||
PB_LTYPE_UINT32 = 0x00,
|
||||
PB_LTYPE_INT32 = 0x00,
|
||||
PB_LTYPE_SINT32 = 0x01,
|
||||
PB_LTYPE_FIXED32 = 0x02,
|
||||
PB_LTYPE_SFIXED32 = 0x02,
|
||||
PB_LTYPE_UINT64 = 0x03,
|
||||
PB_LTYPE_INT64 = 0x03,
|
||||
PB_LTYPE_SINT64 = 0x04,
|
||||
PB_LTYPE_FIXED64 = 0x05,
|
||||
PB_LTYPE_SFIXED64 = 0x05,
|
||||
PB_LTYPE_BOOL = 0x06,
|
||||
PB_LTYPE_ENUM = 0x07,
|
||||
|
||||
// Standard float types
|
||||
PB_ACT_FLOAT,
|
||||
PB_ACT_DOUBLE,
|
||||
PB_LTYPE_FLOAT = 0x08,
|
||||
PB_LTYPE_DOUBLE = 0x09,
|
||||
|
||||
// Constant-sized array
|
||||
PB_ACT_BYTES,
|
||||
// Byte array with pre-allocated buffer.
|
||||
// data_size is the length of the allocated PB_BYTES_ARRAY structure.
|
||||
PB_LTYPE_BYTES = 0x0A,
|
||||
|
||||
// Constant-sized array, with null termination
|
||||
PB_ACT_STRING,
|
||||
// String with pre-allocated buffer.
|
||||
// data_size is the maximum length.
|
||||
PB_LTYPE_STRING = 0x0B,
|
||||
|
||||
// Callback function pointer in field value
|
||||
PB_ACT_SUBMESSAGE,
|
||||
// Submessage
|
||||
// submsg_fields is pointer to field descriptions
|
||||
PB_LTYPE_SUBMESSAGE = 0x0C,
|
||||
|
||||
PB_LAST_ACT
|
||||
} pb_action_t;
|
||||
/////////////
|
||||
// Modifier flags
|
||||
|
||||
// This structure is used in constants to specify struct fields.
|
||||
typedef struct {
|
||||
int field_number;
|
||||
uint16_t offset;
|
||||
pb_action_t action;
|
||||
uint8_t fieldsize;
|
||||
} pb_field_t;
|
||||
// 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;
|
||||
|
||||
#define PB_HTYPE(x) ((x) & 0xF0)
|
||||
#define PB_LTYPE(x) ((x) & 0x0F)
|
||||
|
||||
/* This structure is used in auto-generated constants
|
||||
* to specify struct fields.
|
||||
* You can change field sizes here if you need structures
|
||||
* 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}
|
||||
|
||||
/* --- 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.
|
||||
// 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;
|
||||
typedef PB_BYTES_ARRAY(1) pb_bytes_array_t;
|
||||
|
||||
// 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;
|
||||
struct _pb_callback_t {
|
||||
union {
|
||||
|
||||
367
pb_decode.c
367
pb_decode.c
@@ -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>
|
||||
*/
|
||||
|
||||
#include "pb.h"
|
||||
#include "pb_decode.h"
|
||||
#include <string.h>
|
||||
|
||||
const pb_decoder_t PB_DECODERS[PB_LAST_ACT] = {
|
||||
NULL,
|
||||
const pb_decoder_t PB_DECODERS[16] = {
|
||||
&pb_dec_uint32,
|
||||
&pb_dec_sint32,
|
||||
&pb_dec_uint32, // Cast to int32
|
||||
&pb_dec_fixed32,
|
||||
&pb_dec_fixed32, // Cast to int32
|
||||
&pb_dec_uint64,
|
||||
&pb_dec_sint64,
|
||||
&pb_dec_uint64, // Cast to int64
|
||||
&pb_dec_fixed64,
|
||||
&pb_dec_fixed64, // Cast to int64
|
||||
&pb_dec_bool,
|
||||
&pb_dec_enum,
|
||||
|
||||
&pb_dec_float,
|
||||
&pb_dec_double,
|
||||
|
||||
&pb_dec_bytes,
|
||||
&pb_dec_string,
|
||||
&pb_dec_submessage
|
||||
};
|
||||
|
||||
enum wire_type {
|
||||
WT_VARINT = 0,
|
||||
WT_64BIT = 1,
|
||||
WT_STRING = 2,
|
||||
WT_32BIT = 5
|
||||
};
|
||||
////////////////////////
|
||||
// Helper functions
|
||||
////////////////////////
|
||||
|
||||
// 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)
|
||||
static bool buf_read(pb_istream_t *stream, uint8_t *buf, size_t count)
|
||||
{
|
||||
char byte;
|
||||
int bitpos = 0;
|
||||
*dest = 0;
|
||||
uint8_t *source = (uint8_t*)stream->state;
|
||||
|
||||
while (bitpos < 32 && pb_read(stream, &byte, 1))
|
||||
{
|
||||
*dest |= (byte & 0x7F) << bitpos;
|
||||
bitpos += 7;
|
||||
if (buf != NULL)
|
||||
memcpy(buf, source, count);
|
||||
|
||||
if (!(byte & 0x80))
|
||||
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;
|
||||
}
|
||||
|
||||
bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
|
||||
{
|
||||
uint64_t temp;
|
||||
if (!pb_decode_varint64(stream, &temp))
|
||||
return false;
|
||||
*dest = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pb_decode_varint64(pb_istream_t *stream, uint64_t *dest)
|
||||
{
|
||||
char byte;
|
||||
uint8_t byte;
|
||||
int bitpos = 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)
|
||||
{
|
||||
char byte;
|
||||
uint8_t byte;
|
||||
do
|
||||
{
|
||||
if (!pb_read(stream, &byte, 1))
|
||||
@@ -91,65 +93,226 @@ bool pb_skip_string(pb_istream_t *stream)
|
||||
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)
|
||||
{
|
||||
uint32_t temp;
|
||||
if (!pb_decode_varint32(stream, &temp))
|
||||
return false;
|
||||
|
||||
int field_number = temp >> 3;
|
||||
int tag = temp >> 3;
|
||||
int wire_type = temp & 7;
|
||||
|
||||
const pb_field_t *field = fields;
|
||||
while (field->field_number != 0)
|
||||
i = 0;
|
||||
while (fields[i].tag != 0 && fields[i].tag != tag)
|
||||
{
|
||||
if (field->field_number != field_number)
|
||||
i++;
|
||||
}
|
||||
|
||||
if (fields[i].tag == 0) // No match found, skip data
|
||||
{
|
||||
field++;
|
||||
skip(stream, wire_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
void *destfield = dest + field->offset;
|
||||
fields_seen |= 1 << (i & 31);
|
||||
|
||||
if (field->action == PB_ACT_HAS)
|
||||
{
|
||||
*(bool*)destfield = true;
|
||||
field++;
|
||||
continue;
|
||||
}
|
||||
|
||||
pb_decoder_t func = PB_DECODERS[field->action];
|
||||
if (!func(stream, field, destfield))
|
||||
if (!decode_field(stream, wire_type, &fields[i], dest_struct))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (field->field_number == 0) // No match found, skip data
|
||||
// Check that all required fields (mod 31) were present.
|
||||
for (i = 0; fields[i].tag != 0; i++)
|
||||
{
|
||||
bool status = false;
|
||||
switch (wire_type)
|
||||
if (PB_HTYPE(fields[i].type) == PB_HTYPE_REQUIRED &&
|
||||
!(fields_seen & (1 << (i & 31))))
|
||||
{
|
||||
case WT_VARINT:
|
||||
status = pb_skip_varint(stream);
|
||||
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)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -157,6 +320,8 @@ bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Field decoders */
|
||||
|
||||
bool pb_dec_uint32(pb_istream_t *stream, const pb_field_t *field, void *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)
|
||||
{
|
||||
char bytes[4] = {0};
|
||||
uint8_t bytes[4] = {0};
|
||||
bool status = pb_read(stream, bytes, 4);
|
||||
*(uint32_t*)dest =
|
||||
bytes[0] | ((uint32_t)bytes[1] << 8) |
|
||||
((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24);
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
uint8_t lebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
|
||||
memcpy(dest, lebytes, 4);
|
||||
#else
|
||||
memcpy(dest, bytes, 4);
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
char bytes[8] = {0};
|
||||
uint8_t bytes[8] = {0};
|
||||
bool status = pb_read(stream, bytes, 8);
|
||||
*(uint64_t*)dest =
|
||||
(uint64_t)bytes[0] | ((uint64_t)bytes[1] << 8) |
|
||||
((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) |
|
||||
((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) |
|
||||
((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56);
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4],
|
||||
bytes[3], bytes[2], bytes[1], bytes[0]};
|
||||
memcpy(dest, lebytes, 4);
|
||||
#else
|
||||
memcpy(dest, bytes, 4);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -213,25 +385,37 @@ bool pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
pb_bytearray_t *x = (pb_bytearray_t*)dest;
|
||||
pb_bytes_array_t *x = (pb_bytes_array_t*)dest;
|
||||
|
||||
uint32_t temp;
|
||||
if (!pb_decode_varint32(stream, &temp))
|
||||
return false;
|
||||
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 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))
|
||||
return false;
|
||||
|
||||
if (size > field->fieldsize - 1)
|
||||
if (size > field->data_size - 1)
|
||||
return false;
|
||||
|
||||
bool status = pb_read(stream, (char*)dest, size);
|
||||
*((char*)dest + size) = 0;
|
||||
bool status = pb_read(stream, (uint8_t*)dest, size);
|
||||
*((uint8_t*)dest + size) = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
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)
|
||||
return pb_skip_string(stream);
|
||||
|
||||
uint32_t size;
|
||||
if (!pb_decode_varint32(stream, &size))
|
||||
if (!make_string_substream(stream, &substream))
|
||||
return false;
|
||||
|
||||
if (stream->bytes_left < size)
|
||||
if (field->ptr == NULL)
|
||||
return false;
|
||||
|
||||
// Make a limited-length istream for decoding submessage
|
||||
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;
|
||||
return pb_decode(&substream, (pb_field_t*)field->ptr, dest);
|
||||
}
|
||||
|
||||
21
pb_decode.h
21
pb_decode.h
@@ -6,12 +6,14 @@
|
||||
|
||||
// Decode from stream to destination struct.
|
||||
// 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 ---
|
||||
* 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_varint64(pb_istream_t *stream, uint64_t *dest);
|
||||
|
||||
@@ -20,10 +22,10 @@ bool pb_skip_string(pb_istream_t *stream);
|
||||
|
||||
/* --- Field decoders ---
|
||||
* 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_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);
|
||||
@@ -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_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_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_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);
|
||||
|
||||
// Null-terminated string.
|
||||
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);
|
||||
|
||||
typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest);
|
||||
|
||||
/* --- 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
|
||||
|
||||
@@ -9,8 +9,6 @@ typedef enum {
|
||||
Person_PhoneType_MOBILE = 0,
|
||||
Person_PhoneType_HOME = 1,
|
||||
Person_PhoneType_WORK = 2,
|
||||
|
||||
_Person_PhoneType_size = 0xFFFFFFFF // Force 32-bit enum
|
||||
} Person_PhoneType;
|
||||
|
||||
typedef struct {
|
||||
@@ -24,52 +22,71 @@ typedef struct {
|
||||
int32_t id;
|
||||
bool has_email;
|
||||
char email[40];
|
||||
|
||||
pb_callback_t phone;
|
||||
size_t phone_size;
|
||||
Person_PhoneNumber phone[5];
|
||||
} Person;
|
||||
|
||||
/* Field descriptions */
|
||||
#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[] = {
|
||||
{1, offsetof(Person_PhoneNumber, number), PB_ACT_STRING, membersize(Person_PhoneNumber, number)},
|
||||
{2, offsetof(Person_PhoneNumber, has_type), PB_ACT_HAS, membersize(Person_PhoneNumber, has_type)},
|
||||
{2, offsetof(Person_PhoneNumber, type), PB_ACT_UINT32, membersize(Person_PhoneNumber, type)},
|
||||
{1, PB_HTYPE_REQUIRED | PB_LTYPE_STRING,
|
||||
offsetof(Person_PhoneNumber, number), 0,
|
||||
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
|
||||
};
|
||||
|
||||
const pb_field_t Person_fields[] = {
|
||||
{1, offsetof(Person, name), PB_ACT_STRING, membersize(Person, name)},
|
||||
{2, offsetof(Person, id), PB_ACT_INT32, membersize(Person, id)},
|
||||
{3, offsetof(Person, email), PB_ACT_STRING, membersize(Person, email)},
|
||||
{4, offsetof(Person, phone), PB_ACT_SUBMESSAGE, membersize(Person, phone)}
|
||||
};
|
||||
{1, PB_HTYPE_REQUIRED | PB_LTYPE_STRING,
|
||||
offsetof(Person, name), 0,
|
||||
membersize(Person, name), 0, 0},
|
||||
|
||||
/* Default value descriptions */
|
||||
#define Person_PhoneNumber_default {"", false, Person_PhoneType_HOME};
|
||||
#define Person_default {"", 0, false, "", {{0},0}};
|
||||
{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
|
||||
};
|
||||
|
||||
/* 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)
|
||||
{
|
||||
Person x = Person_default;
|
||||
x.phone.funcs.decode = &print_phonenumber;
|
||||
int i;
|
||||
Person person;
|
||||
|
||||
if (!pb_decode(stream, Person_fields, &x))
|
||||
if (!pb_decode(stream, Person_fields, &person))
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -91,10 +108,10 @@ bool my_read(pb_istream_t *stream, char *buf, size_t count)
|
||||
|
||||
int main()
|
||||
{
|
||||
char buffer[512];
|
||||
uint8_t buffer[512];
|
||||
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))
|
||||
printf("Parsing failed.\n");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user