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 <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 {
|
||||||
|
|||||||
391
pb_decode.c
391
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>
|
* 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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
21
pb_decode.h
21
pb_decode.h
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user