From 35e5c248bee19703b7e3c9e43d7bd8fd7aa2a79d Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Mon, 14 Jun 2010 10:47:56 -0700 Subject: Work to make upb_def consume a upb_src. --- src/upb_def.c | 498 ++++++++++++++++++++++++++-------------------------------- 1 file changed, 219 insertions(+), 279 deletions(-) (limited to 'src/upb_def.c') diff --git a/src/upb_def.c b/src/upb_def.c index e770025..9f34b42 100644 --- a/src/upb_def.c +++ b/src/upb_def.c @@ -18,6 +18,29 @@ static int div_round_up(int numerator, int denominator) { return numerator > 0 ? (numerator - 1) / denominator + 1 : 0; } +// A little dynamic array for storing a growing list of upb_defs. +typedef struct { + upb_def **defs; + uint32_t len; + uint32_t size; +}; + +static void upb_deflist_init(upb_deflist *l) { + l->size = 8 + l->defs = malloc(l->size); + l->len = 0; +} + +static void upb_deflist_uninit(upb_deflist *l) { free(l->defs); } + +static void upb_deflist_push(upb_deflist *l, upb_def *d) { + if(l->defs_len == l->defs_size) { + l->defs_size *= 2; + l->defs = realloc(l->defs, l->defs_size); + } + l->defs[l->defs_len++] = d; +} + /* upb_def ********************************************************************/ // Defs are reference counted, but can have cycles when types are @@ -153,7 +176,7 @@ static void upb_def_init(upb_def *def, enum upb_def_type type, def->type = type; def->is_cyclic = 0; // We detect this later, after resolving refs. def->search_depth = 0; - def->fqname = upb_string_getref(fqname, UPB_REF_FROZEN); + def->fqname = NULL; upb_atomic_refcount_init(&def->refcount, 1); } @@ -340,49 +363,6 @@ typedef struct { upb_strptr string; } iton_ent; -static void insert_enum_value(upb_src *src, upb_enumdef *e) -{ - upb_src_startmsg(src); - int32_t number = -1; - upb_string *name = NULL; - while((f = upb_src_getdef(src)) != NULL) { - switch(f->field_number) { - case GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NUMBER_FIELDNUM: - upb_src_getval(src, &number); - break; - case GOOGLE_PROTOBUF_ENUMVALUDESCRIPTORPROTO_NAME_FIELDNUM: - upb_src_getval(src, &name); - break; - default: - upb_src_skipval(src); - } - } - upb_src_endmsg(src); - ntoi_ent ntoi_ent = {{value->name, 0}, value->number}; - iton_ent iton_ent = {{value->number, 0}, value->name}; - upb_strtable_insert(&e->ntoi, &ntoi_ent.e); - upb_inttable_insert(&e->iton, &iton_ent.e); -} - -static upb_enumdef *enumdef_new(upb_src *src, upb_strptr fqname) -{ - upb_enumdef *e = malloc(sizeof(*e)); - upb_def_init(&e->base, UPB_DEF_ENUM, fqname); - upb_strtable_init(&e->ntoi, 0, sizeof(ntoi_ent)); - upb_inttable_init(&e->iton, 0, sizeof(iton_ent)); - upb_src_startmsg(src); - - upb_fielddef *f; - while((f = upb_src_getdef(src)) != NULL) { - if(f->number == GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_FIELDNUM) { - insert_enum_value(src, e); - } else { - upb_src_skipval(src); - } - } - return e; -} - static void enumdef_free(upb_enumdef *e) { upb_strtable_free(&e->ntoi); upb_inttable_free(&e->iton); @@ -420,7 +400,7 @@ bool upb_enum_done(upb_enum_iter *iter) { typedef struct { upb_strtable_entry e; upb_def *def; -} symtab_ent; +} upb_symtab_ent; /* Search for a character in a string, in reverse. */ static int my_memrchr(char *data, char c, size_t len) @@ -469,7 +449,7 @@ static symtab_ent *resolve(upb_strtable *t, upb_strptr base, upb_strptr symbol) * join("Foo.Bar", "Baz") -> "Foo.Bar.Baz" * join("", "Baz") -> "Baz" * Caller owns a ref on the returned string. */ -static upb_strptr join(upb_strptr base, upb_strptr name) { +static upb_string *upb_join(upb_string *base, upb_string *name) { upb_strptr joined = upb_strdup(base); upb_strlen_t len = upb_strlen(joined); if(len > 0) { @@ -479,201 +459,123 @@ static upb_strptr join(upb_strptr base, upb_strptr name) { return joined; } -static upb_strptr try_define(upb_strtable *t, upb_strptr base, - upb_strptr name, upb_status *status) +static void upb_addenum_val(upb_src *src, upb_enumdef *e, upb_status *status) { - if(upb_string_isnull(name)) { - upb_seterr(status, UPB_STATUS_ERROR, - "symbol in context '" UPB_STRFMT "' does not have a name", - UPB_STRARG(base)); - return UPB_STRING_NULL; - } - upb_strptr fqname = join(base, name); - if(upb_strtable_lookup(t, fqname)) { - upb_seterr(status, UPB_STATUS_ERROR, - "attempted to redefine symbol '" UPB_STRFMT "'", - UPB_STRARG(fqname)); - upb_string_unref(fqname); - return UPB_STRING_NULL; + upb_src_startmsg(src); + int32_t number = -1; + upb_string *name = NULL; + while((f = upb_src_getdef(src)) != NULL) { + switch(f->field_number) { + case GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NUMBER_FIELDNUM: + upb_src_getval(src, &number); + break; + case GOOGLE_PROTOBUF_ENUMVALUDESCRIPTORPROTO_NAME_FIELDNUM: + upb_src_getval(src, &name); + break; + default: + upb_src_skipval(src); + } } - return fqname; + upb_src_endmsg(src); + ntoi_ent ntoi_ent = {{value->name, 0}, value->number}; + iton_ent iton_ent = {{value->number, 0}, value->name}; + upb_strtable_insert(&e->ntoi, &ntoi_ent.e); + upb_inttable_insert(&e->iton, &iton_ent.e); } -static void insert_enum(upb_strtable *t, - google_protobuf_EnumDescriptorProto *ed, - upb_strptr base, upb_status *status) +static void upb_addenum(upb_src *src, upb_deflist *defs, upb_status *status) { - upb_strptr name = ed->set_flags.has.name ? ed->name : UPB_STRING_NULL; - upb_strptr fqname = try_define(t, base, name, status); - if(upb_string_isnull(fqname)) return; - - symtab_ent e; - e.e.key = fqname; - e.def = UPB_UPCAST(enumdef_new(ed, fqname)); - upb_strtable_insert(t, &e.e); - upb_string_unref(fqname); -} + upb_enumdef *e = malloc(sizeof(*e)); + upb_def_init(&e->base, UPB_DEF_ENUM, fqname); + upb_strtable_init(&e->ntoi, 0, sizeof(ntoi_ent)); + upb_inttable_init(&e->iton, 0, sizeof(iton_ent)); + CHECK(upb_src_startmsg(src)); -static void insert_message(upb_strtable *t, google_protobuf_DescriptorProto *d, - upb_strptr base, bool sort, upb_status *status) -{ - upb_strptr name = d->set_flags.has.name ? d->name : UPB_STRING_NULL; - upb_strptr fqname = try_define(t, base, name, status); - if(upb_string_isnull(fqname)) return; - - int num_fields = d->set_flags.has.field ? - google_protobuf_FieldDescriptorProto_array_len(d->field) : 0; - symtab_ent e; - e.e.key = fqname; - - // Gather our list of fields, sorting if necessary. - upb_fielddef **fielddefs = malloc(sizeof(*fielddefs) * num_fields); - for (int i = 0; i < num_fields; i++) { - google_protobuf_FieldDescriptorProto *fd = - google_protobuf_FieldDescriptorProto_array_get(d->field, i); - fielddefs[i] = fielddef_new(fd); + upb_fielddef *f; + while((f = upb_src_getdef(src)) != NULL) { + switch(f->field_number) { + case GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_FIELDNUM: + CHECK(upb_addenum_val(src, e, status)); + break; + default: + upb_src_skipval(src); + break; + } } - if(sort) fielddef_sort(fielddefs, num_fields); - - // Create the msgdef with that list of fields. - e.def = UPB_UPCAST(msgdef_new(fielddefs, num_fields, fqname, status)); - - // Cleanup. - for (int i = 0; i < num_fields; i++) fielddef_free(fielddefs[i]); - free(fielddefs); - - if(!upb_ok(status)) goto error; - - upb_strtable_insert(t, &e.e); - - /* Add nested messages and enums. */ - if(d->set_flags.has.nested_type) - for(unsigned int i = 0; i < google_protobuf_DescriptorProto_array_len(d->nested_type); i++) - insert_message(t, google_protobuf_DescriptorProto_array_get(d->nested_type, i), fqname, sort, status); - - if(d->set_flags.has.enum_type) - for(unsigned int i = 0; i < google_protobuf_EnumDescriptorProto_array_len(d->enum_type); i++) - insert_enum(t, google_protobuf_EnumDescriptorProto_array_get(d->enum_type, i), fqname, status); - -error: - // Free the ref we got from try_define(). - upb_string_unref(fqname); + upb_deflist_push(e); } -static bool find_cycles(upb_msgdef *m, int search_depth, upb_status *status) +// Processes a google.protobuf.DescriptorProto, adding defs to "deflist." +static void upb_addmsg(upb_src *src, upb_deflist *deflist, upb_status *status) { - if(search_depth > UPB_MAX_TYPE_DEPTH) { - // There are many situations in upb where we recurse over the type tree - // (like for example, right now) and an absurdly deep tree could cause us - // to stack overflow on systems with very limited stacks. - upb_seterr(status, UPB_STATUS_ERROR, "Type " UPB_STRFMT " was found at " - "depth %d in the type graph, which exceeds the maximum type " - "depth of %d.", UPB_UPCAST(m)->fqname, search_depth, - UPB_MAX_TYPE_DEPTH); - return false; - } else if(UPB_UPCAST(m)->search_depth == 1) { - // Cycle! - int cycle_len = search_depth - 1; - if(cycle_len > UPB_MAX_TYPE_CYCLE_LEN) { - upb_seterr(status, UPB_STATUS_ERROR, "Type " UPB_STRFMT " was involved " - "in a cycle of length %d, which exceeds the maximum type " - "cycle length of %d.", UPB_UPCAST(m)->fqname, cycle_len, - UPB_MAX_TYPE_CYCLE_LEN); + upb_msgdef *m = malloc(sizeof(*m)); + upb_def_init(&m->base, UPB_DEF_MSG); + upb_atomic_refcount_init(&m->cycle_refcount, 0); + upb_inttable_init(&m->itof, num_fields, sizeof(upb_itof_ent)); + upb_strtable_init(&m->ntof, num_fields, sizeof(upb_ntof_ent)); + m->num_fields = 0; + m->fields = malloc(sizeof(upb_fielddef) * num_fields); + int32_t start_count = defs->len; + + CHECK(upb_src_startmsg(src)); + upb_fielddef *f; + while((f = upb_src_getdef(src)) != NULL) { + switch(f->field_number) { + case GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_FIELDNUM: + upb_string_unref(m->fqname); + CHECK(upb_src_getval(src, &m->fqname)); + break; + case GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_FIELD_NUM: + CHECK(upb_addfield(src, m)); + break; + case GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NESTED_TYPE_NUM: + CHECK(upb_addmsg(src, deflist)); + break; + case GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_NUM: + CHECK(upb_addenum(src, deflist)); + break; + default: + // TODO: extensions. + upb_src_skipval(src); } - return true; - } else if(UPB_UPCAST(m)->search_depth > 0) { - // This was a cycle, but did not originate from the base of our search tree. - // We'll find it when we call find_cycles() on this node directly. + } + CHECK(upb_src_eof(src) && upb_src_endmsg(src)); + if(!m->fqname) { + upb_seterr(status, UPB_STATUS_ERROR, "Encountered message with no name."); return false; - } else { - UPB_UPCAST(m)->search_depth = ++search_depth; - bool cycle_found = false; - for(upb_field_count_t i = 0; i < m->num_fields; i++) { - upb_fielddef *f = &m->fields[i]; - if(!upb_issubmsg(f)) continue; - upb_def *sub_def = f->def; - upb_msgdef *sub_m = upb_downcast_msgdef(sub_def); - if(find_cycles(sub_m, search_depth, status)) { - cycle_found = true; - UPB_UPCAST(m)->is_cyclic = true; - if(f->owned) { - upb_atomic_unref(&sub_def->refcount); - f->owned = false; - } - } - } - UPB_UPCAST(m)->search_depth = 0; - return cycle_found; } + upb_qualify(defs, m->fqname, start_count); + upb_deflist_push(m); + return true; } -static void addfd(upb_strtable *addto, upb_strtable *existingdefs, - google_protobuf_FileDescriptorProto *fd, bool sort, - upb_status *status) +// Processes a google.protobuf.FileDescriptorProto, adding the defs to "defs". +static void upb_addfd(upb_src *src, upb_deflist *defs, upb_status *status) { - upb_strptr pkg; - if(fd->set_flags.has.package) { - pkg = upb_string_getref(fd->package, UPB_REF_FROZEN); - } else { - pkg = upb_string_new(); - } - - if(fd->set_flags.has.message_type) - for(unsigned int i = 0; i < google_protobuf_DescriptorProto_array_len(fd->message_type); i++) - insert_message(addto, google_protobuf_DescriptorProto_array_get(fd->message_type, i), pkg, sort, status); - - if(fd->set_flags.has.enum_type) - for(unsigned int i = 0; i < google_protobuf_EnumDescriptorProto_array_len(fd->enum_type); i++) - insert_enum(addto, google_protobuf_EnumDescriptorProto_array_get(fd->enum_type, i), pkg, status); - - upb_string_unref(pkg); - - if(!upb_ok(status)) { - // TODO: make sure we don't leak any memory in this case. - return; - } - - /* TODO: handle extensions and services. */ - - // Attempt to resolve all references. - symtab_ent *e; - for(e = upb_strtable_begin(addto); e; e = upb_strtable_next(addto, &e->e)) { - upb_msgdef *m = upb_dyncast_msgdef(e->def); - if(!m) continue; - upb_strptr base = e->e.key; - for(upb_field_count_t i = 0; i < m->num_fields; i++) { - upb_fielddef *f = &m->fields[i]; - if(!upb_hasdef(f)) continue; // No resolving necessary. - upb_strptr name = upb_downcast_unresolveddef(f->def)->name; - symtab_ent *found = resolve(existingdefs, base, name); - if(!found) found = resolve(addto, base, name); - upb_field_type_t expected = upb_issubmsg(f) ? UPB_DEF_MSG : UPB_DEF_ENUM; - if(!found) { - upb_seterr(status, UPB_STATUS_ERROR, - "could not resolve symbol '" UPB_STRFMT "'" - " in context '" UPB_STRFMT "'", - UPB_STRARG(name), UPB_STRARG(base)); - return; - } else if(found->def->type != expected) { - upb_seterr(status, UPB_STATUS_ERROR, "Unexpected type"); - return; - } - upb_msgdef_resolve(m, f, found->def); + CHECK(upb_src_startmsg(src)); + upb_string *package = NULL; + upb_fielddef *f; + while((f = upb_src_getdef(src)) != NULL) { + switch(f->field_number) { + case GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_FIELDNUM: + upb_string_unref(package); + CHECK(upb_src_getval(src, &package)); + break; + case GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_NUM: + CHECK(upb_addmsg(src, defs)); + break; + case GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_NUM: + CHECK(upb_addenum(src, defs)); + break; + default: + // TODO: services and extensions. + upb_src_skipval(src); } } + CHECK(upb_src_eof(src) && upb_src_endmsg(src)); - // Deal with type cycles. - for(e = upb_strtable_begin(addto); e; e = upb_strtable_next(addto, &e->e)) { - upb_msgdef *m = upb_dyncast_msgdef(e->def); - if(!m) continue; - - // Do an initial pass over the graph to check that there are no cycles - // longer than the maximum length. We also mark all cyclic defs as such, - // and decrement refs on cyclic defs. - find_cycles(m, 0, status); - upb_msgdef *open_defs[UPB_MAX_TYPE_CYCLE_LEN]; - cycle_ref_or_unref(m, NULL, open_defs, 0, true); - } + upb_qualify(deflist, package, 0); + upb_string_unref(package); } /* upb_symtab *****************************************************************/ @@ -684,25 +586,6 @@ upb_symtab *upb_symtab_new() upb_atomic_refcount_init(&s->refcount, 1); upb_rwlock_init(&s->lock); upb_strtable_init(&s->symtab, 16, sizeof(symtab_ent)); - upb_strtable_init(&s->psymtab, 16, sizeof(symtab_ent)); - - // Add descriptor.proto types to private symtable so we can parse descriptors. - // We know there is only 1. - google_protobuf_FileDescriptorProto *fd = - google_protobuf_FileDescriptorProto_array_get(upb_file_descriptor_set->file, 0); - upb_status status = UPB_STATUS_INIT; - addfd(&s->psymtab, &s->symtab, fd, false, &status); - if(!upb_ok(&status)) { - fprintf(stderr, "Failed to initialize upb: %s.\n", status.msg); - assert(false); - return NULL; // Indicates that upb is buggy or corrupt. - } - upb_static_string name = - UPB_STATIC_STRING_INIT("google.protobuf.FileDescriptorSet"); - upb_strptr nameptr = UPB_STATIC_STRING_PTR_INIT(name); - symtab_ent *e = upb_strtable_lookup(&s->psymtab, nameptr); - assert(e); - s->fds_msgdef = upb_downcast_msgdef(e->def); return s; } @@ -770,53 +653,110 @@ upb_def *upb_symtab_resolve(upb_symtab *s, upb_strptr base, upb_strptr symbol) { return ret; } -void upb_symtab_addfds(upb_symtab *s, google_protobuf_FileDescriptorSet *fds, - upb_status *status) +static bool upb_symtab_findcycles(upb_msgdef *m, int search_depth, upb_status *status) { - if(fds->set_flags.has.file) { - // Insert new symbols into a temporary table until we have verified that - // the descriptor is valid. - upb_strtable tmp; - upb_strtable_init(&tmp, 0, sizeof(symtab_ent)); - - { // Read lock scope - upb_rwlock_rdlock(&s->lock); - for(uint32_t i = 0; i < google_protobuf_FileDescriptorProto_array_len(fds->file); i++) { - addfd(&tmp, &s->symtab, google_protobuf_FileDescriptorProto_array_get(fds->file, i), true, status); - if(!upb_ok(status)) { - free_symtab(&tmp); - upb_rwlock_unlock(&s->lock); - return; + if(search_depth > UPB_MAX_TYPE_DEPTH) { + // There are many situations in upb where we recurse over the type tree + // (like for example, right now) and an absurdly deep tree could cause us + // to stack overflow on systems with very limited stacks. + upb_seterr(status, UPB_STATUS_ERROR, "Type " UPB_STRFMT " was found at " + "depth %d in the type graph, which exceeds the maximum type " + "depth of %d.", UPB_UPCAST(m)->fqname, search_depth, + UPB_MAX_TYPE_DEPTH); + return false; + } else if(UPB_UPCAST(m)->search_depth == 1) { + // Cycle! + int cycle_len = search_depth - 1; + if(cycle_len > UPB_MAX_TYPE_CYCLE_LEN) { + upb_seterr(status, UPB_STATUS_ERROR, "Type " UPB_STRFMT " was involved " + "in a cycle of length %d, which exceeds the maximum type " + "cycle length of %d.", UPB_UPCAST(m)->fqname, cycle_len, + UPB_MAX_TYPE_CYCLE_LEN); + } + return true; + } else if(UPB_UPCAST(m)->search_depth > 0) { + // This was a cycle, but did not originate from the base of our search tree. + // We'll find it when we call find_cycles() on this node directly. + return false; + } else { + UPB_UPCAST(m)->search_depth = ++search_depth; + bool cycle_found = false; + for(upb_field_count_t i = 0; i < m->num_fields; i++) { + upb_fielddef *f = &m->fields[i]; + if(!upb_issubmsg(f)) continue; + upb_def *sub_def = f->def; + upb_msgdef *sub_m = upb_downcast_msgdef(sub_def); + if(find_cycles(sub_m, search_depth, status)) { + cycle_found = true; + UPB_UPCAST(m)->is_cyclic = true; + if(f->owned) { + upb_atomic_unref(&sub_def->refcount); + f->owned = false; } } - upb_rwlock_unlock(&s->lock); } + UPB_UPCAST(m)->search_depth = 0; + return cycle_found; + } +} + +// Given a list of defs, a list of extensions (in the future), and a flag +// indicating whether the new defs can overwrite existing defs in the symtab, +// attempts to add the given defs to the symtab. The whole operation either +// succeeds or fails. Ownership of "defs" and "exts" is taken. +bool upb_symtab_add_defs(upb_symtab *s, upb_deflist *defs, bool allow_redef, + upb_status *status) +{ + // Build a table, for duplicate detection and name resolution. + - // Everything was successfully added, copy from the tmp symtable. - { // Write lock scope - upb_rwlock_wrlock(&s->lock); - symtab_ent *e; - for(e = upb_strtable_begin(&tmp); e; e = upb_strtable_next(&tmp, &e->e)) { - // We checked for duplicates when we had only the read lock, but it is - // theoretically possible that a duplicate symbol when we dropped the - // read lock to acquire a write lock. - if(upb_strtable_lookup(&s->symtab, e->e.key)) { - upb_seterr(status, UPB_STATUS_ERROR, "Attempted to insert duplicate " - "symbol: " UPB_STRFMT, UPB_STRARG(e->e.key)); - // To truly handle this situation we would need to remove any symbols - // from tmp that were successfully inserted into s->symtab. Because - // this case is exceedingly unlikely, and because our hashtable - // doesn't support deletions right now, we leave them in there, which - // means we must not call free_symtab(&s->symtab), so we will leak it. - break; + // Attempt to resolve all references. + { // Write lock scope. + symtab_ent *e; + for(e = upb_strtable_begin(addto); e; e = upb_strtable_next(addto, &e->e)) { + upb_msgdef *m = upb_dyncast_msgdef(e->def); + if(!m) continue; + upb_strptr base = e->e.key; + for(upb_field_count_t i = 0; i < m->num_fields; i++) { + upb_fielddef *f = &m->fields[i]; + if(!upb_hasdef(f)) continue; // No resolving necessary. + upb_strptr name = upb_downcast_unresolveddef(f->def)->name; + symtab_ent *found = resolve(existingdefs, base, name); + if(!found) found = resolve(addto, base, name); + upb_field_type_t expected = upb_issubmsg(f) ? UPB_DEF_MSG : UPB_DEF_ENUM; + if(!found) { + upb_seterr(status, UPB_STATUS_ERROR, + "could not resolve symbol '" UPB_STRFMT "'" + " in context '" UPB_STRFMT "'", + UPB_STRARG(name), UPB_STRARG(base)); + return; + } else if(found->def->type != expected) { + upb_seterr(status, UPB_STATUS_ERROR, "Unexpected type"); + return; } - upb_strtable_insert(&s->symtab, &e->e); + upb_msgdef_resolve(m, f, found->def); } - upb_rwlock_unlock(&s->lock); } - upb_strtable_free(&tmp); + + // Deal with type cycles. + for(e = upb_strtable_begin(addto); e; e = upb_strtable_next(addto, &e->e)) { + upb_msgdef *m = upb_dyncast_msgdef(e->def); + if(!m) continue; + + // Do an initial pass over the graph to check that there are no cycles + // longer than the maximum length. We also mark all cyclic defs as such, + // and decrement refs on cyclic defs. + find_cycles(m, 0, status); + upb_msgdef *open_defs[UPB_MAX_TYPE_CYCLE_LEN]; + cycle_ref_or_unref(m, NULL, open_defs, 0, true); + } + + // Add all defs to the symtab. } - return; +} + +void upb_symtab_addfds(upb_symtab *s, upb_src *src, upb_status *status) +{ } void upb_symtab_add_desc(upb_symtab *s, upb_strptr desc, upb_status *status) -- cgit v1.2.3