Separate PB_HTYPE to PB_ATYPE and PB_HTYPE.

Also clean up the logic so that it is easier to implement more
allocation types in the future.

Update issue 53
Status: FixedInGit
This commit is contained in:
Petteri Aimonen
2013-02-20 22:55:59 +02:00
parent 69085d9387
commit 41f98343c8
3 changed files with 179 additions and 125 deletions

39
pb.h
View File

@@ -84,29 +84,24 @@ typedef uint8_t pb_type_t;
#define PB_LTYPES_COUNT 7 #define PB_LTYPES_COUNT 7
#define PB_LTYPE_MASK 0x0F #define PB_LTYPE_MASK 0x0F
/****************** /**************************
* Modifier flags * * Field repetition rules *
******************/ **************************/
/* Just the basic, write data at data_offset */
#define PB_HTYPE_REQUIRED 0x00 #define PB_HTYPE_REQUIRED 0x00
/* Write true at size_offset */
#define PB_HTYPE_OPTIONAL 0x10 #define PB_HTYPE_OPTIONAL 0x10
/* Read to pre-allocated array
* Maximum number of entries is array_size,
* actual number is stored at size_offset */
#define PB_HTYPE_REPEATED 0x20 #define PB_HTYPE_REPEATED 0x20
#define PB_HTYPE_MASK 0x30
/* Works for all required/optional/repeated fields. /********************
* data_offset points to pb_callback_t structure. * Allocation types *
* LTYPE should be valid or 0 (it is ignored, but ********************/
* sometimes used to speculatively index an array). */
#define PB_HTYPE_CALLBACK 0x30
#define PB_HTYPE_MASK 0xF0 #define PB_ATYPE_STATIC 0x00
#define PB_ATYPE_CALLBACK 0x40
#define PB_ATYPE_MASK 0xC0
#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) #define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) #define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
@@ -214,19 +209,19 @@ typedef enum {
* submessages and default values. * submessages and default values.
*/ */
#define PB_REQUIRED_STATIC(tag, st, m, pm, ltype, ptr) \ #define PB_REQUIRED_STATIC(tag, st, m, pm, ltype, ptr) \
{tag, PB_HTYPE_REQUIRED | ltype, \ {tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr} pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
/* Optional fields add the delta to the has_ variable. */ /* Optional fields add the delta to the has_ variable. */
#define PB_OPTIONAL_STATIC(tag, st, m, pm, ltype, ptr) \ #define PB_OPTIONAL_STATIC(tag, st, m, pm, ltype, ptr) \
{tag, PB_HTYPE_OPTIONAL | ltype, \ {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
pb_delta_end(st, m, pm), \ pb_delta_end(st, m, pm), \
pb_delta(st, has_ ## m, m), \ pb_delta(st, has_ ## m, m), \
pb_membersize(st, m), 0, ptr} pb_membersize(st, m), 0, ptr}
/* Repeated fields have a _count field and also the maximum number of entries. */ /* Repeated fields have a _count field and also the maximum number of entries. */
#define PB_REPEATED_STATIC(tag, st, m, pm, ltype, ptr) \ #define PB_REPEATED_STATIC(tag, st, m, pm, ltype, ptr) \
{tag, PB_HTYPE_REPEATED | ltype, \ {tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, \
pb_delta_end(st, m, pm), \ pb_delta_end(st, m, pm), \
pb_delta(st, m ## _count, m), \ pb_delta(st, m ## _count, m), \
pb_membersize(st, m[0]), \ pb_membersize(st, m[0]), \
@@ -234,15 +229,15 @@ typedef enum {
/* Callbacks are much like required fields except with special datatype. */ /* Callbacks are much like required fields except with special datatype. */
#define PB_REQUIRED_CALLBACK(tag, st, m, pm, ltype, ptr) \ #define PB_REQUIRED_CALLBACK(tag, st, m, pm, ltype, ptr) \
{tag, PB_HTYPE_CALLBACK | ltype, \ {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr} pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
#define PB_OPTIONAL_CALLBACK(tag, st, m, pm, ltype, ptr) \ #define PB_OPTIONAL_CALLBACK(tag, st, m, pm, ltype, ptr) \
{tag, PB_HTYPE_CALLBACK | ltype, \ {tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr} pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
#define PB_REPEATED_CALLBACK(tag, st, m, pm, ltype, ptr) \ #define PB_REPEATED_CALLBACK(tag, st, m, pm, ltype, ptr) \
{tag, PB_HTYPE_CALLBACK | ltype, \ {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \
pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr} pb_delta_end(st, m, pm), 0, pb_membersize(st, m), 0, ptr}
/* The mapping from protobuf types to LTYPEs is done using these macros. */ /* The mapping from protobuf types to LTYPEs is done using these macros. */

View File

@@ -303,8 +303,11 @@ static bool pb_field_next(pb_field_iterator_t *iter)
bool notwrapped = true; bool notwrapped = true;
size_t prev_size = iter->current->data_size; size_t prev_size = iter->current->data_size;
if (PB_HTYPE(iter->current->type) == PB_HTYPE_REPEATED) if (PB_ATYPE(iter->current->type) == PB_ATYPE_STATIC &&
PB_HTYPE(iter->current->type) == PB_HTYPE_REPEATED)
{
prev_size *= iter->current->array_size; prev_size *= iter->current->array_size;
}
if (PB_HTYPE(iter->current->type) == PB_HTYPE_REQUIRED) if (PB_HTYPE(iter->current->type) == PB_HTYPE_REQUIRED)
iter->required_field_index++; iter->required_field_index++;
@@ -343,11 +346,15 @@ static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag)
* Decode a single field * * Decode a single field *
*************************/ *************************/
static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter) static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
{ {
pb_decoder_t func = PB_DECODERS[PB_LTYPE(iter->current->type)]; pb_type_t type;
pb_decoder_t func;
switch (PB_HTYPE(iter->current->type)) type = iter->current->type;
func = PB_DECODERS[PB_LTYPE(type)];
switch (PB_HTYPE(type))
{ {
case PB_HTYPE_REQUIRED: case PB_HTYPE_REQUIRED:
return func(stream, iter->current, iter->pData); return func(stream, iter->current, iter->pData);
@@ -358,7 +365,7 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
case PB_HTYPE_REPEATED: case PB_HTYPE_REPEATED:
if (wire_type == PB_WT_STRING if (wire_type == PB_WT_STRING
&& PB_LTYPE(iter->current->type) <= PB_LTYPE_LAST_PACKABLE) && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
{ {
/* Packed array */ /* Packed array */
bool status = true; bool status = true;
@@ -396,47 +403,62 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t
return func(stream, iter->current, pItem); return func(stream, iter->current, pItem);
} }
case PB_HTYPE_CALLBACK: default:
PB_RETURN_ERROR(stream, "invalid field type");
}
}
static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
{
pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
if (pCallback->funcs.decode == NULL)
return pb_skip_field(stream, wire_type);
if (wire_type == PB_WT_STRING)
{
pb_istream_t substream;
if (!pb_make_string_substream(stream, &substream))
return false;
while (substream.bytes_left)
{ {
pb_callback_t *pCallback = (pb_callback_t*)iter->pData; if (!pCallback->funcs.decode(&substream, iter->current, pCallback->arg))
PB_RETURN_ERROR(stream, "callback failed");
if (pCallback->funcs.decode == NULL)
return pb_skip_field(stream, wire_type);
if (wire_type == PB_WT_STRING)
{
pb_istream_t substream;
if (!pb_make_string_substream(stream, &substream))
return false;
while (substream.bytes_left)
{
if (!pCallback->funcs.decode(&substream, iter->current, pCallback->arg))
PB_RETURN_ERROR(stream, "callback failed");
}
pb_close_string_substream(stream, &substream);
return true;
}
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. */
pb_istream_t substream;
uint8_t buffer[10];
size_t size = sizeof(buffer);
if (!read_raw_value(stream, wire_type, buffer, &size))
return false;
substream = pb_istream_from_buffer(buffer, size);
return pCallback->funcs.decode(&substream, iter->current, pCallback->arg);
}
} }
pb_close_string_substream(stream, &substream);
return true;
}
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. */
pb_istream_t substream;
uint8_t buffer[10];
size_t size = sizeof(buffer);
if (!read_raw_value(stream, wire_type, buffer, &size))
return false;
substream = pb_istream_from_buffer(buffer, size);
return pCallback->funcs.decode(&substream, iter->current, pCallback->arg);
}
}
static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
{
switch (PB_ATYPE(iter->current->type))
{
case PB_ATYPE_STATIC:
return decode_static_field(stream, wire_type, iter);
case PB_ATYPE_CALLBACK:
return decode_callback_field(stream, wire_type, iter);
default: default:
PB_RETURN_ERROR(stream, "invalid field type"); PB_RETURN_ERROR(stream, "invalid field type");
} }
@@ -451,37 +473,43 @@ static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_str
/* Initialize size/has fields and apply default values */ /* Initialize size/has fields and apply default values */
do do
{ {
pb_type_t type;
type = iter.current->type;
if (iter.current->tag == 0) if (iter.current->tag == 0)
continue; continue;
/* Initialize the size field for optional/repeated fields to 0. */ if (PB_ATYPE(type) == PB_ATYPE_STATIC)
if (PB_HTYPE(iter.current->type) == PB_HTYPE_OPTIONAL)
{ {
*(bool*)iter.pSize = false; /* Initialize the size field for optional/repeated fields to 0. */
} if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL)
else if (PB_HTYPE(iter.current->type) == PB_HTYPE_REPEATED) {
{ *(bool*)iter.pSize = false;
*(size_t*)iter.pSize = 0; }
continue; /* Array is empty, no need to initialize contents */ else if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
} {
*(size_t*)iter.pSize = 0;
continue; /* Array is empty, no need to initialize contents */
}
/* Initialize field contents to default value */ /* Initialize field contents to default value */
if (PB_HTYPE(iter.current->type) == PB_HTYPE_CALLBACK) if (PB_LTYPE(iter.current->type) == PB_LTYPE_SUBMESSAGE)
{
pb_message_set_to_defaults((const pb_field_t *) iter.current->ptr, iter.pData);
}
else if (iter.current->ptr != NULL)
{
memcpy(iter.pData, iter.current->ptr, iter.current->data_size);
}
else
{
memset(iter.pData, 0, iter.current->data_size);
}
}
else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
{ {
continue; /* Don't overwrite callback */ continue; /* Don't overwrite callback */
} }
else if (PB_LTYPE(iter.current->type) == PB_LTYPE_SUBMESSAGE)
{
pb_message_set_to_defaults((const pb_field_t *) iter.current->ptr, iter.pData);
}
else if (iter.current->ptr != NULL)
{
memcpy(iter.pData, iter.current->ptr, iter.current->data_size);
}
else
{
memset(iter.pData, 0, iter.current->data_size);
}
} while (pb_field_next(&iter)); } while (pb_field_next(&iter));
} }

View File

@@ -153,58 +153,89 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
return true; return true;
} }
bool checkreturn encode_static_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData)
{
pb_encoder_t func;
const void *pSize;
func = PB_ENCODERS[PB_LTYPE(field->type)];
pSize = (const char*)pData + field->size_offset;
switch (PB_HTYPE(field->type))
{
case PB_HTYPE_REQUIRED:
if (!pb_encode_tag_for_field(stream, field))
return false;
if (!func(stream, field, pData))
return false;
break;
case PB_HTYPE_OPTIONAL:
if (*(const bool*)pSize)
{
if (!pb_encode_tag_for_field(stream, field))
return false;
if (!func(stream, field, pData))
return false;
}
break;
case PB_HTYPE_REPEATED:
if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
return false;
break;
default:
return false;
}
return true;
}
bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData)
{
const pb_callback_t *callback = (const pb_callback_t*)pData;
if (callback->funcs.encode != NULL)
{
if (!callback->funcs.encode(stream, field, callback->arg))
return false;
}
return true;
}
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
{ {
const pb_field_t *field = fields; const pb_field_t *field = fields;
const void *pData = src_struct; const void *pData = src_struct;
const void *pSize;
size_t prev_size = 0; size_t prev_size = 0;
while (field->tag != 0) while (field->tag != 0)
{ {
pb_encoder_t func = PB_ENCODERS[PB_LTYPE(field->type)];
pData = (const char*)pData + prev_size + field->data_offset; pData = (const char*)pData + prev_size + field->data_offset;
pSize = (const char*)pData + field->size_offset;
prev_size = field->data_size; prev_size = field->data_size;
if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
prev_size *= field->array_size;
switch (PB_HTYPE(field->type)) /* Special case for static arrays */
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
{ {
case PB_HTYPE_REQUIRED: prev_size *= field->array_size;
if (!pb_encode_tag_for_field(stream, field)) }
return false;
if (!func(stream, field, pData)) switch (PB_ATYPE(field->type))
{
case PB_ATYPE_STATIC:
if (!encode_static_field(stream, field, pData))
return false; return false;
break; break;
case PB_HTYPE_OPTIONAL: case PB_ATYPE_CALLBACK:
if (*(const bool*)pSize) if (!encode_callback_field(stream, field, pData))
{
if (!pb_encode_tag_for_field(stream, field))
return false;
if (!func(stream, field, pData))
return false;
}
break;
case PB_HTYPE_REPEATED:
if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
return false; return false;
break; break;
case PB_HTYPE_CALLBACK: default:
{ return false;
const pb_callback_t *callback = (const pb_callback_t*)pData;
if (callback->funcs.encode != NULL)
{
if (!callback->funcs.encode(stream, field, callback->arg))
return false;
}
break;
}
} }
field++; field++;