diff --git a/Makefile b/Makefile index 6acb9306cc..143673c1b6 100644 --- a/Makefile +++ b/Makefile @@ -185,7 +185,11 @@ descriptorgen: upb/descriptor.pb tools/upbc tools/upbc: tools/upbc.c $(LIBUPB) $(E) CC $< - $(Q) $(CC) $(CFLAGS) $(CPPFLAGS) $(DEF_OPT) -o $@ $< $(LIBUPB) + $(Q) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(LIBUPB) + +examples/msg: examples/msg.c $(LIBUPB) + $(E) CC $< + $(Q) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(LIBUPB) # Tests. ####################################################################### diff --git a/examples/example.proto b/examples/example.proto new file mode 100644 index 0000000000..231c455ca1 --- /dev/null +++ b/examples/example.proto @@ -0,0 +1,15 @@ +// +// upb - a minimalist implementation of protocol buffers. +// +// Copyright (c) 2011 Google Inc. See LICENSE for details. +// Author: Josh Haberman +// +// A .proto file for the examples in this directory. + +package example; + +message SampleMessage { + optional string name = 1; + optional int32 id = 2; + optional string email = 3; +} diff --git a/examples/msg.c b/examples/msg.c new file mode 100644 index 0000000000..487e743602 --- /dev/null +++ b/examples/msg.c @@ -0,0 +1,59 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2011 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * A simple example that demonstrates creating a standard message object + * and parsing into it, using a dynamic reflection-based approach. + * + * Note that with this approach there are no strongly-typed struct or class + * definitions to use from C -- this is essentially a reflection-based + * interface. Note that parsing and serializing are still very fast since + * they are JIT-based. + * + * If this seems a bit verbose, you may prefer an approach that generates + * strongly-typed struct definitions (upb will likely provide this, but it is + * not yet implemented). + * + * TODO: make this file compiled as part of "make examples" + * TODO: test that this actually works (WARNING: UNTESTED!) + */ + +#include "upb/msg.h" +#include "upb/pb/glue.h" + +const char *descfile = "example.proto.pb"; +const char *type = "example.SampleMessage"; +const char *msgfile = "sample_message.pb"; + +int main() { + // First we load the descriptor that describes the message into a upb_msgdef. + // This could come from a string that is compiled into the program or from a + // separate file as we do here. Since defs always live in a symtab, we + // create one of those also. + upb_symtab *s = upb_symtab_new(); + upb_status status = UPB_STATUS_INIT; + if (!upb_load_descriptor_file_into_symtab(s, descfile, &status)) { + fprintf(stderr, "Couldn't load descriptor file '%s': %s\n", descfile, + upb_status_getstr(&status)); + return -1; + } + + const upb_msgdef *md = upb_symtab_lookupmsg(s, type); + if (!md) { + fprintf(stderr, "Descriptor did not contain type '%s'\n", type); + return -1; + } + + // Parse a file into a new message object. + void *msg = upb_filetonewmsg(msgfile, md, &status); + if (!msg) { + fprintf(stderr, "Error parsing message file '%s': %s\n", msgfile, + upb_status_getstr(&status)); + return -1; + } + + // TODO: Inspect some fields. + return 0; +} diff --git a/tests/test_vs_proto2.cc b/tests/test_vs_proto2.cc index 22aa2e2ada..222bcdb348 100644 --- a/tests/test_vs_proto2.cc +++ b/tests/test_vs_proto2.cc @@ -30,8 +30,8 @@ void compare_arrays(const google::protobuf::Reflection *r, { ASSERT(upb_msg_has(upb_msg, upb_f)); ASSERT(upb_isseq(upb_f)); - void *arr = upb_value_getptr(upb_msg_getseq(upb_msg, upb_f)); - void *iter = upb_seq_begin(arr, upb_f); + const void *arr = upb_value_getptr(upb_msg_getseq(upb_msg, upb_f)); + const void *iter = upb_seq_begin(arr, upb_f); for(int i = 0; i < r->FieldSize(proto2_msg, proto2_f); i++, iter = upb_seq_next(arr, iter, upb_f)) { diff --git a/upb/def.h b/upb/def.h index d3c7c07ca0..ab84376d12 100644 --- a/upb/def.h +++ b/upb/def.h @@ -294,13 +294,13 @@ void upb_msgdef_layout(upb_msgdef *m); // Looks up a field by name or number. While these are written to be as fast // as possible, it will still be faster to cache the results of this lookup if // possible. These return NULL if no such field is found. -INLINE upb_fielddef *upb_msgdef_itof(upb_msgdef *m, uint32_t i) { +INLINE upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) { upb_itof_ent *e = (upb_itof_ent*) upb_inttable_fastlookup(&m->itof, i, sizeof(upb_itof_ent)); return e ? e->f : NULL; } -INLINE upb_fielddef *upb_msgdef_ntof(upb_msgdef *m, const char *name) { +INLINE upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name) { upb_ntof_ent *e = (upb_ntof_ent*)upb_strtable_lookup(&m->ntof, name); return e ? e->f : NULL; } diff --git a/upb/msg.c b/upb/msg.c index ece22ab399..87bb61bdb7 100644 --- a/upb/msg.c +++ b/upb/msg.c @@ -89,9 +89,9 @@ void upb_stdmsg_sethas(void *_m, upb_value fval) { if (f->hasbit >= 0) m[f->hasbit / 8] |= (1 << (f->hasbit % 8)); } -bool upb_stdmsg_has(void *_m, upb_value fval) { +bool upb_stdmsg_has(const void *_m, upb_value fval) { assert(_m != NULL); - char *m = _m; + const char *m = _m; const upb_fielddef *f = upb_value_getfielddef(fval); return f->hasbit < 0 || (m[f->hasbit / 8] & (1 << (f->hasbit % 8))); } @@ -116,15 +116,15 @@ bool upb_stdmsg_has(void *_m, upb_value fval) { return UPB_CONTINUE; \ } \ \ - upb_value upb_stdmsg_get ## type(void *_m, upb_value fval) { \ + upb_value upb_stdmsg_get ## type(const void *_m, upb_value fval) { \ assert(_m != NULL); \ - uint8_t *m = _m; \ + const uint8_t *m = _m; \ const upb_fielddef *f = upb_value_getfielddef(fval); \ upb_value ret; \ upb_value_set ## type(&ret, *(ctype*)&m[f->offset]); \ return ret; \ } \ - upb_value upb_stdmsg_seqget ## type(void *i) { \ + upb_value upb_stdmsg_seqget ## type(const void *i) { \ assert(i != NULL); \ upb_value val; \ upb_value_set ## type(&val, *(ctype*)i); \ @@ -176,12 +176,12 @@ upb_flow_t upb_stdmsg_setstr_r(void *a, upb_value fval, upb_value val) { return UPB_CONTINUE; } -upb_value upb_stdmsg_getstr(void *m, upb_value fval) { +upb_value upb_stdmsg_getstr(const void *m, upb_value fval) { assert(m != NULL); return upb_stdmsg_getptr(m, fval); } -upb_value upb_stdmsg_seqgetstr(void *i) { +upb_value upb_stdmsg_seqgetstr(const void *i) { assert(i != NULL); return upb_stdmsg_seqgetptr(i); } @@ -275,15 +275,15 @@ upb_sflow_t upb_stdmsg_startsubmsg_r(void *a, upb_value fval) { return UPB_CONTINUE_WITH(*subm); } -void *upb_stdmsg_seqbegin(void *_a) { - upb_stdarray *a = _a; +const void *upb_stdmsg_seqbegin(const void *_a) { + const upb_stdarray *a = _a; return a->len > 0 ? a->ptr : NULL; } #define NEXTFUNC(size) \ - void *upb_stdmsg_ ## size ## byte_seqnext(void *_a, void *iter) { \ - upb_stdarray *a = _a; \ - void *next = (char*)iter + size; \ + const void *upb_stdmsg_ ## size ## byte_seqnext(const void *_a, const void *iter) {\ + const upb_stdarray *a = _a; \ + const void *next = (char*)iter + size; \ return (char*)next < (char*)a->ptr + (a->len * size) ? next : NULL; \ } diff --git a/upb/msg.h b/upb/msg.h index fa53056d79..67903d0425 100644 --- a/upb/msg.h +++ b/upb/msg.h @@ -40,13 +40,13 @@ extern "C" { // for one specific upb_fielddef. Each field has a separate accessor, which // lives in the fielddef. -typedef bool upb_has_reader(void *m, upb_value fval); -typedef upb_value upb_value_reader(void *m, upb_value fval); +typedef bool upb_has_reader(const void *m, upb_value fval); +typedef upb_value upb_value_reader(const void *m, upb_value fval); -typedef void *upb_seqbegin_handler(void *s); -typedef void *upb_seqnext_handler(void *s, void *iter); -typedef upb_value upb_seqget_handler(void *iter); -INLINE bool upb_seq_done(void *iter) { return iter == NULL; } +typedef const void *upb_seqbegin_handler(const void *s); +typedef const void *upb_seqnext_handler(const void *s, const void *iter); +typedef upb_value upb_seqget_handler(const void *iter); +INLINE bool upb_seq_done(const void *iter) { return iter == NULL; } typedef struct _upb_accessor_vtbl { // Writers. These take an fval as a parameter because the callbacks are used @@ -97,18 +97,18 @@ INLINE void upb_msg_clearbit(void *msg, const upb_fielddef *f) { // or arrays. Also this could be desired to provide proto2 operations on // generated messages. -INLINE bool upb_msg_has(void *m, const upb_fielddef *f) { +INLINE bool upb_msg_has(const void *m, const upb_fielddef *f) { return f->accessor && f->accessor->has(m, f->fval); } // May only be called for fields that have accessors. -INLINE upb_value upb_msg_get(void *m, const upb_fielddef *f) { +INLINE upb_value upb_msg_get(const void *m, const upb_fielddef *f) { assert(f->accessor && !upb_isseq(f)); return f->accessor->get(m, f->fval); } // May only be called for fields that have accessors. -INLINE upb_value upb_msg_getseq(void *m, const upb_fielddef *f) { +INLINE upb_value upb_msg_getseq(const void *m, const upb_fielddef *f) { assert(f->accessor && upb_isseq(f)); return f->accessor->getseq(m, f->fval); } @@ -118,21 +118,36 @@ INLINE void upb_msg_set(void *m, const upb_fielddef *f, upb_value val) { f->accessor->set(m, f->fval, val); } -INLINE void *upb_seq_begin(void *s, const upb_fielddef *f) { +INLINE const void *upb_seq_begin(const void *s, const upb_fielddef *f) { assert(f->accessor); return f->accessor->seqbegin(s); } -INLINE void *upb_seq_next(void *s, void *iter, const upb_fielddef *f) { +INLINE const void *upb_seq_next(const void *s, const void *iter, + const upb_fielddef *f) { assert(f->accessor); assert(!upb_seq_done(iter)); return f->accessor->seqnext(s, iter); } -INLINE upb_value upb_seq_get(void *iter, const upb_fielddef *f) { +INLINE upb_value upb_seq_get(const void *iter, const upb_fielddef *f) { assert(f->accessor); assert(!upb_seq_done(iter)); return f->accessor->seqget(iter); } +INLINE bool upb_msg_has_named(const void *m, const upb_msgdef *md, + const char *field_name) { + const upb_fielddef *f = upb_msgdef_ntof(md, field_name); + return f && upb_msg_has(m, f); +} + +INLINE bool upb_msg_get_named(const void *m, const upb_msgdef *md, + const char *field_name, upb_value *val) { + const upb_fielddef *f = upb_msgdef_ntof(md, field_name); + if (!f) return false; + *val = upb_msg_get(m, f); + return true; +} + /* upb_msgvisitor *************************************************************/ @@ -264,30 +279,30 @@ upb_sflow_t upb_stdmsg_startsubmsg_r(void *c, upb_value fval); /* Standard readers. **********************************************************/ -bool upb_stdmsg_has(void *c, upb_value fval); -void *upb_stdmsg_seqbegin(void *c); - -upb_value upb_stdmsg_getint64(void *c, upb_value fval); -upb_value upb_stdmsg_getint32(void *c, upb_value fval); -upb_value upb_stdmsg_getuint64(void *c, upb_value fval); -upb_value upb_stdmsg_getuint32(void *c, upb_value fval); -upb_value upb_stdmsg_getdouble(void *c, upb_value fval); -upb_value upb_stdmsg_getfloat(void *c, upb_value fval); -upb_value upb_stdmsg_getbool(void *c, upb_value fval); -upb_value upb_stdmsg_getptr(void *c, upb_value fval); - -void *upb_stdmsg_8byte_seqnext(void *c, void *iter); -void *upb_stdmsg_4byte_seqnext(void *c, void *iter); -void *upb_stdmsg_1byte_seqnext(void *c, void *iter); - -upb_value upb_stdmsg_seqgetint64(void *c); -upb_value upb_stdmsg_seqgetint32(void *c); -upb_value upb_stdmsg_seqgetuint64(void *c); -upb_value upb_stdmsg_seqgetuint32(void *c); -upb_value upb_stdmsg_seqgetdouble(void *c); -upb_value upb_stdmsg_seqgetfloat(void *c); -upb_value upb_stdmsg_seqgetbool(void *c); -upb_value upb_stdmsg_seqgetptr(void *c); +bool upb_stdmsg_has(const void *c, upb_value fval); +const void *upb_stdmsg_seqbegin(const void *c); + +upb_value upb_stdmsg_getint64(const void *c, upb_value fval); +upb_value upb_stdmsg_getint32(const void *c, upb_value fval); +upb_value upb_stdmsg_getuint64(const void *c, upb_value fval); +upb_value upb_stdmsg_getuint32(const void *c, upb_value fval); +upb_value upb_stdmsg_getdouble(const void *c, upb_value fval); +upb_value upb_stdmsg_getfloat(const void *c, upb_value fval); +upb_value upb_stdmsg_getbool(const void *c, upb_value fval); +upb_value upb_stdmsg_getptr(const void *c, upb_value fval); + +const void *upb_stdmsg_8byte_seqnext(const void *c, const void *iter); +const void *upb_stdmsg_4byte_seqnext(const void *c, const void *iter); +const void *upb_stdmsg_1byte_seqnext(const void *c, const void *iter); + +upb_value upb_stdmsg_seqgetint64(const void *c); +upb_value upb_stdmsg_seqgetint32(const void *c); +upb_value upb_stdmsg_seqgetuint64(const void *c); +upb_value upb_stdmsg_seqgetuint32(const void *c); +upb_value upb_stdmsg_seqgetdouble(const void *c); +upb_value upb_stdmsg_seqgetfloat(const void *c); +upb_value upb_stdmsg_seqgetbool(const void *c); +upb_value upb_stdmsg_seqgetptr(const void *c); #ifdef __cplusplus } /* extern "C" */ diff --git a/upb/pb/glue.c b/upb/pb/glue.c index b364a6df6d..37b86d969e 100644 --- a/upb/pb/glue.c +++ b/upb/pb/glue.c @@ -30,6 +30,20 @@ void upb_strtomsg(const char *str, size_t len, void *msg, const upb_msgdef *md, upb_decoder_uninit(&d); } +void *upb_filetonewmsg(const char *fname, const upb_msgdef *md, upb_status *s) { + void *msg = upb_stdmsg_new(md); + size_t len; + char *data = upb_readfile(fname, &len); + if (!data) goto err; + upb_strtomsg(data, len, msg, md, s); + if (!upb_ok(s)) goto err; + return msg; + +err: + upb_stdmsg_free(msg, md); + return NULL; +} + #if 0 void upb_msgtotext(upb_string *str, upb_msg *msg, upb_msgdef *md, bool single_line) { diff --git a/upb/pb/glue.h b/upb/pb/glue.h index 7aa4a5f15f..38e8d8ec06 100644 --- a/upb/pb/glue.h +++ b/upb/pb/glue.h @@ -28,21 +28,20 @@ #include #include "upb/upb.h" +#include "upb/def.h" #ifdef __cplusplus extern "C" { #endif -// Forward-declares so we don't have to include everything in this .h file. -// Clients should use the regular, typedef'd names (eg. upb_string). -struct _upb_msg; -struct _upb_msgdef; -struct _upb_symtab; - // Decodes the given string, which must be in protobuf binary format, to the // given upb_msg with msgdef "md", storing the status of the operation in "s". void upb_strtomsg(const char *str, size_t len, void *msg, - const struct _upb_msgdef *md, upb_status *s); + const upb_msgdef *md, upb_status *s); + +// Parses the given file into a new message of the given type. Caller owns +// the returned message (or NULL if an error occurred). +void *upb_filetonewmsg(const char *fname, const upb_msgdef *md, upb_status *s); //void upb_msgtotext(struct _upb_string *str, void *msg, // struct _upb_msgdef *md, bool single_line); @@ -56,12 +55,12 @@ upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, upb_status *status); // Like the previous but also adds the loaded defs to the given symtab. -bool upb_load_descriptor_into_symtab(struct _upb_symtab *symtab, const char *str, +bool upb_load_descriptor_into_symtab(upb_symtab *symtab, const char *str, size_t len, upb_status *status); // Like the previous but also reads the descriptor from the given filename. -bool upb_load_descriptor_file_into_symtab(struct _upb_symtab *symtab, - const char *fname, upb_status *status); +bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, + upb_status *status); // Reads the given filename into a character string, returning NULL if there // was an error. diff --git a/upb/table.h b/upb/table.h index a8c2015912..f4104578dc 100644 --- a/upb/table.h +++ b/upb/table.h @@ -149,9 +149,10 @@ INLINE size_t _upb_inttable_entrysize(size_t value_size) { return upb_align_up(sizeof(upb_inttable_header) + value_size, 8); } -INLINE void *upb_inttable_fastlookup(upb_inttable *t, uint32_t key, - uint32_t value_size) { - return _upb_inttable_fastlookup(t, key, _upb_inttable_entrysize(value_size), value_size); +INLINE void *upb_inttable_fastlookup(const upb_inttable *t, uint32_t key, + uint32_t value_size) { + return _upb_inttable_fastlookup( + t, key, _upb_inttable_entrysize(value_size), value_size); } INLINE void *upb_inttable_lookup(upb_inttable *t, uint32_t key) {