diff --git a/pbstream.c b/pbstream.c index dd91bb8888..10f149c929 100644 --- a/pbstream.c +++ b/pbstream.c @@ -91,6 +91,9 @@ static pbstream_status_t get_f_uint64_t(char **buf, char *end, uint64_t *val) static int32_t zz_decode_32(uint32_t n) { return (n >> 1) ^ -(int32_t)(n & 1); } static int64_t zz_decode_64(uint64_t n) { return (n >> 1) ^ -(int64_t)(n & 1); } +/* Functions for reading wire values and converting them to values. These + * are generated with macros because they follow a higly consistent pattern. */ + #define CHECK(func) do { \ pbstream_wire_type_t status = func; \ if(status != PBSTREAM_STATUS_OK) return status; \ @@ -106,11 +109,14 @@ static int64_t zz_decode_64(uint64_t n) { return (n >> 1) ^ -(int64_t)(n & 1); } * pbstream_status_t get_TYPE(char **buf, char *end, size_t offset, * pbstream_value *dst) */ #define GET(type, v_or_f, wire_t, val_t, member_name) \ - static pbstream_status_t get_ ## type(char **buf, char *end, size_t offset, \ + static pbstream_status_t get_ ## type(struct pbstream_parse_state *s, \ + char *buf, char *end, \ struct pbstream_value *d) { \ wire_t tmp; \ - CHECK(get_ ## v_or_f ## _ ## wire_t(buf, end, &tmp)); \ - wvtov_ ## type(tmp, &d->v.member_name, offset); \ + char *b = buf; \ + CHECK(get_ ## v_or_f ## _ ## wire_t(&b, end, &tmp)); \ + wvtov_ ## type(tmp, &d->v.member_name, s->offset); \ + s->offset += (b-buf); \ return PBSTREAM_STATUS_OK; \ } @@ -134,22 +140,61 @@ T(SFIXED64, f, uint64_t, int64_t, int64) { *d = (int64_t)s; } T(BOOL, v, uint32_t, bool, _bool) { *d = (bool)s; } T(ENUM, v, uint32_t, int32_t, _enum) { *d = (int32_t)s; } -#define T_DELIMITED(type) \ - T(type, v, uint32_t, struct pbstream_delimited, delimited) { \ +#define WVTOV_DELIMITED(type) \ + WVTOV(type, uint32_t, struct pbstream_delimited) { \ d->offset = offset; \ d->len = s; \ } -T_DELIMITED(STRING); /* We leave UTF-8 validation to the client. */ -T_DELIMITED(BYTES); -T_DELIMITED(MESSAGE); +WVTOV_DELIMITED(STRING); +WVTOV_DELIMITED(BYTES); +WVTOV_DELIMITED(MESSAGE); #undef WVTOV #undef GET #undef T #undef T_DELIMITED +static pbstream_status_t get_STRING(struct pbstream_parse_state *s, + char *buf, char *end, + struct pbstream_value *d) { + uint32_t tmp; + CHECK(get_v_uint32_t(&buf, end, &tmp)); + wvtov_STRING(tmp, &d->v.delimited, s->offset); + s->offset = d->v.delimited.offset + d->v.delimited.len; /* skip string */ + /* we leave UTF-8 validation to the client. */ + return PBSTREAM_STATUS_OK; +} + +static pbstream_status_t get_BYTES(struct pbstream_parse_state *s, + char *buf, char *end, + struct pbstream_value *d) { + uint32_t tmp; + CHECK(get_v_uint32_t(&buf, end, &tmp)); + wvtov_BYTES(tmp, &d->v.delimited, s->offset); + s->offset = d->v.delimited.offset + d->v.delimited.len; /* skip bytes */ + return PBSTREAM_STATUS_OK; +} + +static pbstream_status_t get_MESSAGE(struct pbstream_parse_state *s, + char *buf, char *end, + struct pbstream_value *d) { + /* We're entering a sub-message. */ + uint32_t tmp; + CHECK(get_v_uint32_t(&buf, end, &tmp)); + wvtov_MESSAGE(tmp, &d->v.delimited, s->offset); + s->offset = d->v.delimited.offset; /* skip past only the tag. */ + RESIZE_DYNARRAY(s->stack, s->stack_len+1); + struct pbstream_parse_stack_frame *frame = DYNARRAY_GET_TOP(s->stack); + frame->message_descriptor = d->field_descriptor->d.message; + frame->end_offset = d->v.delimited.offset + d->v.delimited.len; + int num_seen_fields = frame->message_descriptor->num_seen_fields; + INIT_DYNARRAY(frame->seen_fields, num_seen_fields, num_seen_fields); + return PBSTREAM_STATUS_OK; +} + struct pbstream_type_info { pbstream_wire_type_t expected_wire_type; - pbstream_status_t (*get)(char **buf, char *end, size_t offset, + pbstream_status_t (*get)(struct pbstream_parse_state *s, + char *buf, char *end, struct pbstream_value *d); }; static struct pbstream_type_info type_info[] = { @@ -228,7 +273,6 @@ static struct pbstream_field_descriptor *find_field_descriptor( return NULL; } -/* Process actions associated with the end of a [sub-]message. */ pbstream_status_t process_message_end(struct pbstream_parse_state *s) { struct pbstream_parse_stack_frame *frame = DYNARRAY_GET_TOP(s->stack); @@ -283,23 +327,9 @@ pbstream_status_t parse_field(struct pbstream_parse_state *s, frame->seen_fields[fd->seen_field_num] = true; } - if(unlikely(fd->type == PBSTREAM_TYPE_MESSAGE)) { - /* We're entering a sub-message. */ - CHECK(info->get(&b, end, val_offset, val)); - RESIZE_DYNARRAY(s->stack, s->stack_len+1); - struct pbstream_parse_stack_frame *frame = DYNARRAY_GET_TOP(s->stack); - frame->message_descriptor = fd->d.message; - frame->end_offset = val->v.delimited.offset + val->v.delimited.len; - s->offset = wv->v.delimited.offset; /* skip past only the tag. */ - int num_seen_fields = frame->message_descriptor->num_seen_fields; - INIT_DYNARRAY(frame->seen_fields, num_seen_fields, num_seen_fields); - } else { - /* This is a scalar value. */ - *fieldnum = tag.field_number; - val->type = fd->type; - CHECK(info->get(&b, end, val_offset, val)); - s->offset += (b-buf); - } + *fieldnum = tag.field_number; + val->field_descriptor = fd; + CHECK(info->get(s, b, end, val)); return PBSTREAM_STATUS_OK; unknown_value: diff --git a/pbstream.h b/pbstream.h index 686692e303..3c551d2a5a 100644 --- a/pbstream.h +++ b/pbstream.h @@ -50,8 +50,9 @@ typedef enum pbstream_cardinality { typedef int32_t pbstream_field_number_t; /* A deserialized value as described in a .proto file. */ +struct pbstream_field_descriptor; struct pbstream_value { - pbstream_type_t type; + struct pbstream_field_descriptor *field_descriptor; union { double _double; float _float; diff --git a/tests.c b/tests.c index 079f32599f..e429188037 100644 --- a/tests.c +++ b/tests.c @@ -8,7 +8,7 @@ void test_get_v_uint64_t() char zero[] = {0x00}; char *zero_buf = zero; - uint64_t zero_val; + uint64_t zero_val = 0; status = get_v_uint64_t(&zero_buf, zero_buf+sizeof(zero), &zero_val); assert(status == PBSTREAM_STATUS_OK); assert(zero_val == 0); @@ -16,34 +16,34 @@ void test_get_v_uint64_t() char one[] = {0x01}; char *one_buf = one; - uint64_t one_val; + uint64_t one_val = 0; status = get_v_uint64_t(&one_buf, one_buf+sizeof(one), &one_val); assert(status == PBSTREAM_STATUS_OK); assert(one_val == 1); char twobyte[] = {0xAC, 0x02}; char *twobyte_buf = twobyte; - uint64_t twobyte_val; + uint64_t twobyte_val = 0; status = get_v_uint64_t(&twobyte_buf, twobyte_buf+sizeof(twobyte), &twobyte_val); assert(status == PBSTREAM_STATUS_OK); assert(twobyte_val == 300); char ninebyte[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F}; char *ninebyte_buf = ninebyte; - uint64_t ninebyte_val; + uint64_t ninebyte_val = 0; status = get_v_uint64_t(&ninebyte_buf, ninebyte_buf+sizeof(ninebyte), &ninebyte_val); assert(status == PBSTREAM_STATUS_OK); assert(ninebyte_val == (1LL<<63)); char overrun[] = {0x80, 0x01}; char *overrun_buf = overrun; - uint64_t overrun_val; + uint64_t overrun_val = 0; status = get_v_uint64_t(&overrun_buf, overrun_buf+sizeof(overrun)-1, &overrun_val); assert(status == PBSTREAM_STATUS_INCOMPLETE); char tenbyte[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01}; char *tenbyte_buf = tenbyte; - uint64_t tenbyte_val; + uint64_t tenbyte_val = 0; status = get_v_uint64_t(&tenbyte_buf, tenbyte_buf+sizeof(tenbyte), &tenbyte_val); assert(status == PBSTREAM_ERROR_UNTERMINATED_VARINT); }