There is currently a memory leak when type definitions form cycles. This will need to be dealt with.pull/13171/head
parent
a95ab58e79
commit
18291eedc3
16 changed files with 779 additions and 824 deletions
@ -1,350 +0,0 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Joshua Haberman. See LICENSE for details. |
||||
*/ |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include "descriptor.h" |
||||
#include "upb_context.h" |
||||
#include "upb_def.h" |
||||
#include "upb_mm.h" |
||||
|
||||
struct upb_symtab_entry { |
||||
struct upb_strtable_entry e; |
||||
struct upb_def *def; |
||||
}; |
||||
|
||||
/* Search for a character in a string, in reverse. */ |
||||
static int my_memrchr(char *data, char c, size_t len) |
||||
{ |
||||
int off = len-1; |
||||
while(off > 0 && data[off] != c) --off; |
||||
return off; |
||||
} |
||||
|
||||
void addfd(struct upb_strtable *addto, struct upb_strtable *existingdefs, |
||||
google_protobuf_FileDescriptorProto *fd, bool sort, |
||||
struct upb_status *status); |
||||
|
||||
struct upb_context *upb_context_new() |
||||
{ |
||||
struct upb_context *c = malloc(sizeof(*c)); |
||||
upb_atomic_refcount_init(&c->refcount, 1); |
||||
upb_rwlock_init(&c->lock); |
||||
upb_strtable_init(&c->symtab, 16, sizeof(struct upb_symtab_entry)); |
||||
upb_strtable_init(&c->psymtab, 16, sizeof(struct upb_symtab_entry)); |
||||
/* Add all the types in descriptor.proto so we can parse descriptors. */ |
||||
google_protobuf_FileDescriptorProto *fd = |
||||
upb_file_descriptor_set->file->elements[0]; /* We know there is only 1. */ |
||||
struct upb_status status = UPB_STATUS_INIT; |
||||
addfd(&c->psymtab, &c->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. */ |
||||
} |
||||
struct upb_string name = UPB_STRLIT("google.protobuf.FileDescriptorSet"); |
||||
struct upb_symtab_entry *e = upb_strtable_lookup(&c->psymtab, &name); |
||||
assert(e); |
||||
c->fds_msgdef = upb_downcast_msgdef(e->def); |
||||
return c; |
||||
} |
||||
|
||||
static void free_symtab(struct upb_strtable *t) |
||||
{ |
||||
struct upb_symtab_entry *e = upb_strtable_begin(t); |
||||
for(; e; e = upb_strtable_next(t, &e->e)) { |
||||
upb_def_unref(e->def); |
||||
upb_string_unref(e->e.key); |
||||
} |
||||
upb_strtable_free(t); |
||||
} |
||||
|
||||
static void free_context(struct upb_context *c) |
||||
{ |
||||
free_symtab(&c->symtab); |
||||
free_symtab(&c->psymtab); |
||||
} |
||||
|
||||
void upb_context_unref(struct upb_context *c) |
||||
{ |
||||
if(upb_atomic_unref(&c->refcount)) { |
||||
upb_rwlock_wrlock(&c->lock); |
||||
free_context(c); |
||||
upb_rwlock_unlock(&c->lock); |
||||
upb_rwlock_destroy(&c->lock); |
||||
free(c); |
||||
} |
||||
} |
||||
|
||||
struct upb_def **upb_context_getandref_defs(struct upb_context *c, int *count) |
||||
{ |
||||
upb_rwlock_wrlock(&c->lock); |
||||
*count = upb_strtable_count(&c->symtab); |
||||
struct upb_def **defs = malloc(sizeof(*defs) * (*count)); |
||||
struct upb_symtab_entry *e = upb_strtable_begin(&c->symtab); |
||||
int i = 0; |
||||
for(; e; e = upb_strtable_next(&c->symtab, &e->e), i++) { |
||||
assert(e->def); |
||||
defs[i] = e->def; |
||||
upb_def_ref(defs[i]); |
||||
} |
||||
assert(*count == i); |
||||
upb_rwlock_unlock(&c->lock); |
||||
return defs; |
||||
} |
||||
|
||||
struct upb_def *upb_context_lookup(struct upb_context *c, |
||||
struct upb_string *sym) |
||||
{ |
||||
upb_rwlock_rdlock(&c->lock); |
||||
struct upb_symtab_entry *e = upb_strtable_lookup(&c->symtab, sym); |
||||
upb_rwlock_unlock(&c->lock); |
||||
return e ? e->def : NULL; |
||||
} |
||||
|
||||
/* Given a symbol and the base symbol inside which it is defined, find the
|
||||
* symbol's definition in t. */ |
||||
static struct upb_symtab_entry *resolve(struct upb_strtable *t, |
||||
struct upb_string *base, |
||||
struct upb_string *symbol) |
||||
{ |
||||
if(base->byte_len + symbol->byte_len + 1 >= UPB_SYMBOL_MAXLEN || |
||||
symbol->byte_len == 0) return NULL; |
||||
|
||||
if(symbol->ptr[0] == UPB_SYMBOL_SEPARATOR) { |
||||
/* Symbols starting with '.' are absolute, so we do a single lookup. */ |
||||
struct upb_string sym_str = {.ptr = symbol->ptr+1, |
||||
.byte_len = symbol->byte_len-1}; |
||||
return upb_strtable_lookup(t, &sym_str); |
||||
} else { |
||||
/* Remove components from base until we find an entry or run out. */ |
||||
char sym[UPB_SYMBOL_MAXLEN+1]; |
||||
struct upb_string sym_str = {.ptr = sym}; |
||||
int baselen = base->byte_len; |
||||
while(1) { |
||||
/* sym_str = base[0...base_len] + UPB_SYMBOL_SEPARATOR + symbol */ |
||||
memcpy(sym, base->ptr, baselen); |
||||
sym[baselen] = UPB_SYMBOL_SEPARATOR; |
||||
memcpy(sym + baselen + 1, symbol->ptr, symbol->byte_len); |
||||
sym_str.byte_len = baselen + symbol->byte_len + 1; |
||||
|
||||
struct upb_symtab_entry *e = upb_strtable_lookup(t, &sym_str); |
||||
if (e) return e; |
||||
else if(baselen == 0) return NULL; /* No more scopes to try. */ |
||||
|
||||
baselen = my_memrchr(base->ptr, UPB_SYMBOL_SEPARATOR, baselen); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Tries to resolve a symbol in two different tables. */ |
||||
struct upb_def *resolve2(struct upb_strtable *t1, struct upb_strtable *t2, |
||||
struct upb_string *base, struct upb_string *sym, |
||||
enum upb_def_type expected_type) { |
||||
struct upb_symtab_entry *e = resolve(t1, base, sym); |
||||
if(e == NULL) e = resolve(t2, base, sym); |
||||
if(e && e->def->type == expected_type) return e->def; |
||||
return NULL; |
||||
} |
||||
|
||||
|
||||
struct upb_def *upb_context_resolve(struct upb_context *c, |
||||
struct upb_string *base, |
||||
struct upb_string *symbol) { |
||||
upb_rwlock_rdlock(&c->lock); |
||||
struct upb_symtab_entry *e = resolve(&c->symtab, base, symbol); |
||||
upb_rwlock_unlock(&c->lock); |
||||
return e ? e->def : NULL; |
||||
} |
||||
|
||||
/* Joins strings together, for example:
|
||||
* join("Foo.Bar", "Baz") -> "Foo.Bar.Baz" |
||||
* join("", "Baz") -> "Baz" |
||||
* Caller owns the returned string and must free it. */ |
||||
static struct upb_string *join(struct upb_string *base, struct upb_string *name) { |
||||
size_t len = base->byte_len + name->byte_len; |
||||
if(base->byte_len > 0) len++; /* For the separator. */ |
||||
struct upb_string *joined = upb_string_new(); |
||||
upb_string_resize(joined, len); |
||||
if(base->byte_len > 0) { |
||||
/* nested_base = base + '.' + d->name */ |
||||
memcpy(joined->ptr, base->ptr, base->byte_len); |
||||
joined->ptr[base->byte_len] = UPB_SYMBOL_SEPARATOR; |
||||
memcpy(&joined->ptr[base->byte_len+1], name->ptr, name->byte_len); |
||||
} else { |
||||
memcpy(joined->ptr, name->ptr, name->byte_len); |
||||
} |
||||
return joined; |
||||
} |
||||
|
||||
static void insert_enum(struct upb_strtable *t, |
||||
google_protobuf_EnumDescriptorProto *ed, |
||||
struct upb_string *base, |
||||
struct upb_status *status) |
||||
{ |
||||
if(!ed->set_flags.has.name) { |
||||
upb_seterr(status, UPB_STATUS_ERROR, |
||||
"enum in context '" UPB_STRFMT "' does not have a name", |
||||
UPB_STRARG(base)); |
||||
return; |
||||
} |
||||
|
||||
struct upb_string *fqname = join(base, ed->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; |
||||
} |
||||
|
||||
struct upb_symtab_entry e; |
||||
e.e.key = fqname; // Donating our ref to the table.
|
||||
e.def = (struct upb_def*)upb_enumdef_new(ed, fqname); |
||||
upb_strtable_insert(t, &e.e); |
||||
} |
||||
|
||||
static void insert_message(struct upb_strtable *t, |
||||
google_protobuf_DescriptorProto *d, |
||||
struct upb_string *base, bool sort, |
||||
struct upb_status *status) |
||||
{ |
||||
if(!d->set_flags.has.name) { |
||||
upb_seterr(status, UPB_STATUS_ERROR, |
||||
"message in context '" UPB_STRFMT "' does not have a name", |
||||
UPB_STRARG(base)); |
||||
return; |
||||
} |
||||
|
||||
/* We own this and must free it on destruct. */ |
||||
struct upb_string *fqname = join(base, d->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; |
||||
} |
||||
|
||||
struct upb_symtab_entry e; |
||||
e.e.key = fqname; // Donating our ref to the table.
|
||||
struct upb_fielddef *fielddefs = malloc(sizeof(*fielddefs) * d->field->len); |
||||
for (unsigned int i = 0; i < d->field->len; i++) { |
||||
google_protobuf_FieldDescriptorProto *fd = d->field->elements[i]; |
||||
upb_fielddef_init(&fielddefs[i], fd); |
||||
} |
||||
if(sort) upb_fielddef_sort(fielddefs, d->field->len); |
||||
e.def = (struct upb_def*)upb_msgdef_new(fielddefs, d->field->len, fqname); |
||||
upb_strtable_insert(t, &e.e); |
||||
|
||||
/* Add nested messages and enums. */ |
||||
if(d->set_flags.has.nested_type) |
||||
for(unsigned int i = 0; i < d->nested_type->len; i++) |
||||
insert_message(t, d->nested_type->elements[i], fqname, sort, status); |
||||
|
||||
if(d->set_flags.has.enum_type) |
||||
for(unsigned int i = 0; i < d->enum_type->len; i++) |
||||
insert_enum(t, d->enum_type->elements[i], fqname, status); |
||||
} |
||||
|
||||
void addfd(struct upb_strtable *addto, struct upb_strtable *existingdefs, |
||||
google_protobuf_FileDescriptorProto *fd, bool sort, |
||||
struct upb_status *status) |
||||
{ |
||||
struct upb_string pkg = {.byte_len=0}; |
||||
if(fd->set_flags.has.package) pkg = *fd->package; |
||||
|
||||
if(fd->set_flags.has.message_type) |
||||
for(unsigned int i = 0; i < fd->message_type->len; i++) |
||||
insert_message(addto, fd->message_type->elements[i], &pkg, sort, status); |
||||
|
||||
if(fd->set_flags.has.enum_type) |
||||
for(unsigned int i = 0; i < fd->enum_type->len; i++) |
||||
insert_enum(addto, fd->enum_type->elements[i], &pkg, status); |
||||
|
||||
if(!upb_ok(status)) return; |
||||
|
||||
/* TODO: handle extensions and services. */ |
||||
|
||||
/* Attempt to resolve all references. */ |
||||
struct upb_symtab_entry *e; |
||||
for(e = upb_strtable_begin(addto); e; e = upb_strtable_next(addto, &e->e)) { |
||||
if(upb_strtable_lookup(existingdefs, e->e.key)) { |
||||
upb_seterr(status, UPB_STATUS_ERROR, |
||||
"attempted to redefine symbol '" UPB_STRFMT "'", |
||||
UPB_STRARG(e->e.key)); |
||||
return; |
||||
} |
||||
if(e->def->type == UPB_DEF_MESSAGE) { |
||||
struct upb_msgdef *m = upb_downcast_msgdef(e->def); |
||||
for(unsigned int i = 0; i < m->num_fields; i++) { |
||||
struct upb_fielddef *f = &m->fields[i]; |
||||
if(!upb_issubmsg(f) && f->type != UPB_TYPENUM(ENUM)) { |
||||
// No resolving necessary.
|
||||
continue; |
||||
} |
||||
struct upb_def *def; |
||||
struct upb_string *name = upb_downcast_unresolveddef(f->def)->name; |
||||
if(upb_issubmsg(f)) |
||||
def = resolve2(existingdefs, addto, e->e.key, name, UPB_DEF_MESSAGE); |
||||
else if(f->type == UPB_TYPENUM(ENUM)) |
||||
def = resolve2(existingdefs, addto, e->e.key, name, UPB_DEF_ENUM); |
||||
if(!def) { |
||||
upb_seterr(status, UPB_STATUS_ERROR, |
||||
"could not resolve symbol '" UPB_STRFMT "'" |
||||
" in context '" UPB_STRFMT "'", |
||||
UPB_STRARG(name), UPB_STRARG(e->e.key)); |
||||
return; |
||||
} |
||||
upb_msgdef_resolve(m, f, def); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void upb_context_addfds(struct upb_context *c, |
||||
google_protobuf_FileDescriptorSet *fds, |
||||
struct 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. */ |
||||
struct upb_strtable tmp; |
||||
upb_strtable_init(&tmp, 0, sizeof(struct upb_symtab_entry)); |
||||
upb_rwlock_rdlock(&c->lock); |
||||
for(uint32_t i = 0; i < fds->file->len; i++) { |
||||
addfd(&tmp, &c->symtab, fds->file->elements[i], true, status); |
||||
if(!upb_ok(status)) { |
||||
free_symtab(&tmp); |
||||
upb_rwlock_unlock(&c->lock); |
||||
return; |
||||
} |
||||
} |
||||
upb_rwlock_unlock(&c->lock); |
||||
|
||||
/* Everything was successfully added, copy from the tmp symtable. */ |
||||
struct upb_symtab_entry *e; |
||||
{ |
||||
upb_rwlock_wrlock(&c->lock); |
||||
for(e = upb_strtable_begin(&tmp); e; e = upb_strtable_next(&tmp, &e->e)) |
||||
upb_strtable_insert(&c->symtab, &e->e); |
||||
upb_rwlock_unlock(&c->lock); |
||||
} |
||||
upb_strtable_free(&tmp); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
void upb_context_parsefds(struct upb_context *c, struct upb_string *fds_str, |
||||
struct upb_status *status) |
||||
{ |
||||
struct upb_msg *fds = upb_msg_new(c->fds_msgdef); |
||||
upb_msg_parsestr(fds, fds_str->ptr, fds_str->byte_len, status); |
||||
if(!upb_ok(status)) return; |
||||
upb_context_addfds(c, (google_protobuf_FileDescriptorSet*)fds, status); |
||||
return; |
||||
} |
@ -1,89 +0,0 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* A context represents a namespace of proto definitions, sort of like an |
||||
* interpreter's symbol table. It is empty when first constructed. Clients |
||||
* add definitions to the context by supplying unserialized or serialized |
||||
* descriptors (as defined in descriptor.proto). |
||||
* |
||||
* Copyright (c) 2009 Joshua Haberman. See LICENSE for details. |
||||
*/ |
||||
|
||||
#ifndef UPB_CONTEXT_H_ |
||||
#define UPB_CONTEXT_H_ |
||||
|
||||
#include "upb.h" |
||||
#include "upb_table.h" |
||||
#include "upb_atomic.h" |
||||
|
||||
struct google_protobuf_FileDescriptorProto; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* Definitions. ***************************************************************/ |
||||
|
||||
struct upb_context { |
||||
upb_atomic_refcount_t refcount; |
||||
upb_rwlock_t lock; // Protects all members except the refcount.
|
||||
struct upb_msgdef *fds_msgdef; // In psymtab, ptr here for convenience.
|
||||
|
||||
// Our symbol tables; we own refs to the defs therein.
|
||||
struct upb_strtable symtab; // The context's symbol table.
|
||||
struct upb_strtable psymtab; // Private symbols, for internal use.
|
||||
}; |
||||
|
||||
// Initializes a upb_context. Contexts are not freed explicitly, but unref'd
|
||||
// when the caller is done with them.
|
||||
struct upb_context *upb_context_new(void); |
||||
INLINE void upb_context_ref(struct upb_context *c) { |
||||
upb_atomic_ref(&c->refcount); |
||||
} |
||||
void upb_context_unref(struct upb_context *c); |
||||
|
||||
/* Looking up symbols. ********************************************************/ |
||||
|
||||
// Resolves the given symbol using the rules described in descriptor.proto,
|
||||
// namely:
|
||||
//
|
||||
// If the name starts with a '.', it is fully-qualified. Otherwise, C++-like
|
||||
// scoping rules are used to find the type (i.e. first the nested types
|
||||
// within this message are searched, then within the parent, on up to the
|
||||
// root namespace).
|
||||
//
|
||||
// Returns NULL if no such symbol has been defined.
|
||||
struct upb_def *upb_context_resolve(struct upb_context *c, |
||||
struct upb_string *base, |
||||
struct upb_string *symbol); |
||||
|
||||
// Find an entry in the symbol table with this exact name. Returns NULL if no
|
||||
// such symbol name has been defined.
|
||||
struct upb_def *upb_context_lookup(struct upb_context *c, |
||||
struct upb_string *sym); |
||||
|
||||
// Gets an array of pointers to all currently active defs in this context. The
|
||||
// caller owns the returned array (which is of length *count) as well as a ref
|
||||
// to each symbol inside.
|
||||
struct upb_def **upb_context_getandref_defs(struct upb_context *c, int *count); |
||||
|
||||
/* Adding symbols. ************************************************************/ |
||||
|
||||
// Adds the definitions in the given file descriptor to this context. All
|
||||
// types that are referenced from fd must have previously been defined (or be
|
||||
// defined in fd). fd may not attempt to define any names that are already
|
||||
// defined in this context. Caller retains ownership of fd. status indicates
|
||||
// whether the operation was successful or not, and the error message (if any).
|
||||
struct google_protobuf_FileDescriptorSet; |
||||
void upb_context_addfds(struct upb_context *c, |
||||
struct google_protobuf_FileDescriptorSet *fds, |
||||
struct upb_status *status); |
||||
// Like the above, but also parses the FileDescriptorSet from fds.
|
||||
void upb_context_parsefds(struct upb_context *c, struct upb_string *fds, |
||||
struct upb_status *status); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UPB_PARSE_H_ */ |
Loading…
Reference in new issue