From e6b461c84af646e3cb93e1c9fc965f2da4b92f12 Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Wed, 25 Feb 2009 22:11:53 -0800 Subject: More tests and bugfixes -- parses its first proto! --- pbstream.c | 30 ++++++++++++++++++++---------- pbstream.h | 8 ++++---- tests.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/pbstream.c b/pbstream.c index c8711e7..82fc4d2 100644 --- a/pbstream.c +++ b/pbstream.c @@ -159,7 +159,9 @@ WVTOV_DELIMITED(MESSAGE); static pbstream_status_t get_STRING(struct pbstream_parse_state *s, char *buf, struct pbstream_value *d) { uint32_t tmp; - CHECK(get_v_uint32_t(&buf, &tmp)); + char *b = buf; + CHECK(get_v_uint32_t(&b, &tmp)); + s->offset += (b-buf); /* advance past length varint. */ 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. */ @@ -169,7 +171,9 @@ static pbstream_status_t get_STRING(struct pbstream_parse_state *s, char *buf, static pbstream_status_t get_BYTES(struct pbstream_parse_state *s, char *buf, struct pbstream_value *d) { uint32_t tmp; - CHECK(get_v_uint32_t(&buf, &tmp)); + char *b = buf; + CHECK(get_v_uint32_t(&b, &tmp)); + s->offset += (b-buf); /* advance past length varint. */ wvtov_BYTES(tmp, &d->v.delimited, s->offset); s->offset = d->v.delimited.offset + d->v.delimited.len; /* skip bytes */ return PBSTREAM_STATUS_OK; @@ -179,9 +183,11 @@ static pbstream_status_t get_MESSAGE(struct pbstream_parse_state *s, char *buf, struct pbstream_value *d) { /* We're entering a sub-message. */ uint32_t tmp; + char *b = buf; CHECK(get_v_uint32_t(&buf, &tmp)); + s->offset += (b-buf); /* advance past length varint. */ wvtov_MESSAGE(tmp, &d->v.delimited, s->offset); - s->offset = d->v.delimited.offset; /* skip past only the tag. */ + /* Unlike STRING and BYTES, we *don't* advance past delimited here. */ if (unlikely(++s->top == s->limit)) { /* Stack has grown beyond its limit, must reallocate. */ int cur_size = s->top - s->base; @@ -254,7 +260,8 @@ static struct pbstream_field *find_field(struct pbstream_fieldset* fs, pbstream_field_number_t num) { /* TODO: a hybrid array/hashtable structure. */ - if(num < fs->num_fields) return &fs->fields[num]; + /* TODO: can zero be a tag number? */ + if(num <= fs->num_fields) return &fs->fields[num-1]; else return NULL; } @@ -277,7 +284,7 @@ pbstream_status_t pbstream_parse_field(struct pbstream_parse_state *s, struct pbstream_tag tag; CHECK(parse_tag(&b, &tag)); - size_t val_offset = s->offset + (b-buf); + s->offset += (b-buf); struct pbstream_field *fd = find_field(s->top->fieldset, tag.field_number); pbstream_status_t unknown_value_status; if(unlikely(!fd)) { @@ -297,22 +304,25 @@ pbstream_status_t pbstream_parse_field(struct pbstream_parse_state *s, unknown_value: wv->type = tag.wire_type; - CHECK(parse_unknown_value(&b, val_offset, wv)); + CHECK(parse_unknown_value(&b, s->offset, wv)); s->offset += (b-buf); return unknown_value_status; } void pbstream_init_parser( struct pbstream_parse_state *state, - struct pbstream_fieldset *toplevel_fieldset, - void *user_data) + struct pbstream_fieldset *toplevel_fieldset) { state->offset = 0; - state->user_data = user_data; - /* Initial stack of <300b most protobufs are unlikely to nest >20 deep. */ + /* Initial stack of <300b, most protobufs are unlikely to nest >20 deep. */ const int initial_stack = 20; state->top = state->base = malloc(sizeof(*state->base) * initial_stack); state->limit = state->base + initial_stack; state->top->fieldset = toplevel_fieldset; state->top->end_offset = SIZE_MAX; } + +void pbstream_free_parser(struct pbstream_parse_state *state) +{ + free(state->base); +} diff --git a/pbstream.h b/pbstream.h index 2713974..89e7329 100644 --- a/pbstream.h +++ b/pbstream.h @@ -9,7 +9,7 @@ /* A list of types as they can appear in a .proto file. */ typedef enum pbstream_type { - PBSTREAM_TYPE_DOUBLE, + PBSTREAM_TYPE_DOUBLE = 0, PBSTREAM_TYPE_FLOAT, PBSTREAM_TYPE_INT32, PBSTREAM_TYPE_INT64, @@ -101,7 +101,6 @@ struct pbstream_parse_stack_frame { /* The stream parser's state. */ struct pbstream_parse_state { size_t offset; - void *user_data; struct pbstream_parse_stack_frame *base, *top, *limit; }; @@ -110,8 +109,9 @@ struct pbstream_parse_state { * unknown. */ void pbstream_init_parser( struct pbstream_parse_state *state, - struct pbstream_fieldset *toplevel_fieldset, - void *user_data); + struct pbstream_fieldset *toplevel_fieldset); + +void pbstream_free_parser(struct pbstream_parse_state *state); /* Status as returned by pbstream_parse(). Status codes <0 are fatal errors * that cannot be recovered. Status codes >0 are unusual but nonfatal events, diff --git a/tests.c b/tests.c index 4277a19..ea87d79 100644 --- a/tests.c +++ b/tests.c @@ -42,9 +42,46 @@ void test_get_v_uint64_t() assert(status == PBSTREAM_ERROR_UNTERMINATED_VARINT); } +void test_simple_proto() +{ + struct pbstream_fieldset *fieldset1 = malloc(sizeof(*fieldset1) + + 2*sizeof(struct pbstream_field)); + fieldset1->num_fields = 2; + fieldset1->fields[0].field_number = 1; + fieldset1->fields[0].type = PBSTREAM_TYPE_INT32; + fieldset1->fields[1].field_number = 2; + fieldset1->fields[1].type = PBSTREAM_TYPE_STRING; + + char message1[] = {0x08, 0x96, 0x01}; + struct pbstream_parse_state s; + pbstream_init_parser(&s, fieldset1); + assert(s.offset == 0); + pbstream_field_number_t fieldnum; + struct pbstream_value val; + struct pbstream_wire_value wv; + assert(pbstream_parse_field(&s, message1, &fieldnum, &val, &wv) == + PBSTREAM_STATUS_OK); + assert(val.field == &fieldset1->fields[0]); + assert(val.v.int32 == 150); + assert(s.offset == 3); + pbstream_free_parser(&s); + + char message2[] = {0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67}; + pbstream_init_parser(&s, fieldset1); + assert(pbstream_parse_field(&s, message2, &fieldnum, &val, &wv) == + PBSTREAM_STATUS_OK); + assert(val.field == &fieldset1->fields[1]); + assert(val.v.delimited.offset == 2); + assert(val.v.delimited.len == 7); + assert(s.offset == 9); + pbstream_free_parser(&s); + free(fieldset1); +} + int main() { test_get_v_uint64_t(); + test_simple_proto(); printf("All tests passed.\n"); return 0; } -- cgit v1.2.3