Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.7 KiB
247 lines
6.7 KiB
|
|
#include "upb/handlers.h" |
|
#include "upb/msgfactory.h" |
|
|
|
static bool is_power_of_two(size_t val) { |
|
return (val & (val - 1)) == 0; |
|
} |
|
|
|
/* Align up to the given power of 2. */ |
|
static size_t align_up(size_t val, size_t align) { |
|
UPB_ASSERT(is_power_of_two(align)); |
|
return (val + align - 1) & ~(align - 1); |
|
} |
|
|
|
static size_t div_round_up(size_t n, size_t d) { |
|
return (n + d - 1) / d; |
|
} |
|
|
|
static size_t upb_msgval_sizeof2(upb_fieldtype_t type) { |
|
switch (type) { |
|
case UPB_TYPE_DOUBLE: |
|
case UPB_TYPE_INT64: |
|
case UPB_TYPE_UINT64: |
|
return 8; |
|
case UPB_TYPE_ENUM: |
|
case UPB_TYPE_INT32: |
|
case UPB_TYPE_UINT32: |
|
case UPB_TYPE_FLOAT: |
|
return 4; |
|
case UPB_TYPE_BOOL: |
|
return 1; |
|
case UPB_TYPE_MESSAGE: |
|
return sizeof(void*); |
|
case UPB_TYPE_BYTES: |
|
case UPB_TYPE_STRING: |
|
return sizeof(upb_stringview); |
|
} |
|
UPB_UNREACHABLE(); |
|
} |
|
|
|
static uint8_t upb_msg_fielddefsize(const upb_fielddef *f) { |
|
if (upb_fielddef_isseq(f)) { |
|
return sizeof(void*); |
|
} else { |
|
return upb_msgval_sizeof2(upb_fielddef_type(f)); |
|
} |
|
} |
|
|
|
|
|
/** upb_msglayout *************************************************************/ |
|
|
|
static void upb_msglayout_free(upb_msglayout *l) { |
|
upb_gfree(l); |
|
} |
|
|
|
static size_t upb_msglayout_place(upb_msglayout *l, size_t size) { |
|
size_t ret; |
|
|
|
l->size = align_up(l->size, size); |
|
ret = l->size; |
|
l->size += size; |
|
return ret; |
|
} |
|
|
|
static bool upb_msglayout_init(const upb_msgdef *m, |
|
upb_msglayout *l, |
|
upb_msgfactory *factory) { |
|
upb_msg_field_iter it; |
|
upb_msg_oneof_iter oit; |
|
size_t hasbit; |
|
size_t submsg_count = 0; |
|
const upb_msglayout **submsgs; |
|
upb_msglayout_field *fields; |
|
|
|
for (upb_msg_field_begin(&it, m); |
|
!upb_msg_field_done(&it); |
|
upb_msg_field_next(&it)) { |
|
const upb_fielddef* f = upb_msg_iter_field(&it); |
|
if (upb_fielddef_issubmsg(f)) { |
|
submsg_count++; |
|
} |
|
} |
|
|
|
memset(l, 0, sizeof(*l)); |
|
|
|
fields = upb_gmalloc(upb_msgdef_numfields(m) * sizeof(*fields)); |
|
submsgs = upb_gmalloc(submsg_count * sizeof(*submsgs)); |
|
|
|
if ((!fields && upb_msgdef_numfields(m)) || |
|
(!submsgs && submsg_count)) { |
|
/* OOM. */ |
|
upb_gfree(fields); |
|
upb_gfree(submsgs); |
|
return false; |
|
} |
|
|
|
l->field_count = upb_msgdef_numfields(m); |
|
l->fields = fields; |
|
l->submsgs = submsgs; |
|
|
|
/* Allocate data offsets in three stages: |
|
* |
|
* 1. hasbits. |
|
* 2. regular fields. |
|
* 3. oneof fields. |
|
* |
|
* OPT: There is a lot of room for optimization here to minimize the size. |
|
*/ |
|
|
|
/* Allocate hasbits and set basic field attributes. */ |
|
submsg_count = 0; |
|
for (upb_msg_field_begin(&it, m), hasbit = 0; |
|
!upb_msg_field_done(&it); |
|
upb_msg_field_next(&it)) { |
|
const upb_fielddef* f = upb_msg_iter_field(&it); |
|
upb_msglayout_field *field = &fields[upb_fielddef_index(f)]; |
|
|
|
field->number = upb_fielddef_number(f); |
|
field->descriptortype = upb_fielddef_descriptortype(f); |
|
field->label = upb_fielddef_label(f); |
|
|
|
if (upb_fielddef_issubmsg(f)) { |
|
const upb_msglayout *sub_layout = |
|
upb_msgfactory_getlayout(factory, upb_fielddef_msgsubdef(f)); |
|
field->submsg_index = submsg_count++; |
|
submsgs[field->submsg_index] = sub_layout; |
|
} |
|
|
|
if (upb_fielddef_haspresence(f) && !upb_fielddef_containingoneof(f)) { |
|
field->presence = (hasbit++); |
|
} else { |
|
field->presence = 0; |
|
} |
|
} |
|
|
|
/* Account for space used by hasbits. */ |
|
l->size = div_round_up(hasbit, 8); |
|
|
|
/* Allocate non-oneof fields. */ |
|
for (upb_msg_field_begin(&it, m); !upb_msg_field_done(&it); |
|
upb_msg_field_next(&it)) { |
|
const upb_fielddef* f = upb_msg_iter_field(&it); |
|
size_t field_size = upb_msg_fielddefsize(f); |
|
size_t index = upb_fielddef_index(f); |
|
|
|
if (upb_fielddef_containingoneof(f)) { |
|
/* Oneofs are handled separately below. */ |
|
continue; |
|
} |
|
|
|
fields[index].offset = upb_msglayout_place(l, field_size); |
|
} |
|
|
|
/* Allocate oneof fields. Each oneof field consists of a uint32 for the case |
|
* and space for the actual data. */ |
|
for (upb_msg_oneof_begin(&oit, m); !upb_msg_oneof_done(&oit); |
|
upb_msg_oneof_next(&oit)) { |
|
const upb_oneofdef* o = upb_msg_iter_oneof(&oit); |
|
upb_oneof_iter fit; |
|
|
|
size_t case_size = sizeof(uint32_t); /* Could potentially optimize this. */ |
|
size_t field_size = 0; |
|
uint32_t case_offset; |
|
uint32_t data_offset; |
|
|
|
/* Calculate field size: the max of all field sizes. */ |
|
for (upb_oneof_begin(&fit, o); |
|
!upb_oneof_done(&fit); |
|
upb_oneof_next(&fit)) { |
|
const upb_fielddef* f = upb_oneof_iter_field(&fit); |
|
field_size = UPB_MAX(field_size, upb_msg_fielddefsize(f)); |
|
} |
|
|
|
/* Align and allocate case offset. */ |
|
case_offset = upb_msglayout_place(l, case_size); |
|
data_offset = upb_msglayout_place(l, field_size); |
|
|
|
for (upb_oneof_begin(&fit, o); |
|
!upb_oneof_done(&fit); |
|
upb_oneof_next(&fit)) { |
|
const upb_fielddef* f = upb_oneof_iter_field(&fit); |
|
fields[upb_fielddef_index(f)].offset = data_offset; |
|
fields[upb_fielddef_index(f)].presence = ~case_offset; |
|
} |
|
} |
|
|
|
/* Size of the entire structure should be a multiple of its greatest |
|
* alignment. TODO: track overall alignment for real? */ |
|
l->size = align_up(l->size, 8); |
|
|
|
return true; |
|
} |
|
|
|
|
|
/** upb_msgfactory ************************************************************/ |
|
|
|
struct upb_msgfactory { |
|
const upb_symtab *symtab; /* We own a ref. */ |
|
upb_inttable layouts; |
|
}; |
|
|
|
upb_msgfactory *upb_msgfactory_new(const upb_symtab *symtab) { |
|
upb_msgfactory *ret = upb_gmalloc(sizeof(*ret)); |
|
|
|
ret->symtab = symtab; |
|
upb_inttable_init(&ret->layouts, UPB_CTYPE_PTR); |
|
|
|
return ret; |
|
} |
|
|
|
void upb_msgfactory_free(upb_msgfactory *f) { |
|
upb_inttable_iter i; |
|
upb_inttable_begin(&i, &f->layouts); |
|
for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { |
|
upb_msglayout *l = upb_value_getptr(upb_inttable_iter_value(&i)); |
|
upb_msglayout_free(l); |
|
} |
|
|
|
upb_inttable_uninit(&f->layouts); |
|
upb_gfree(f); |
|
} |
|
|
|
const upb_symtab *upb_msgfactory_symtab(const upb_msgfactory *f) { |
|
return f->symtab; |
|
} |
|
|
|
const upb_msglayout *upb_msgfactory_getlayout(upb_msgfactory *f, |
|
const upb_msgdef *m) { |
|
upb_value v; |
|
UPB_ASSERT(upb_symtab_lookupmsg(f->symtab, upb_msgdef_fullname(m)) == m); |
|
UPB_ASSERT(!upb_msgdef_mapentry(m)); |
|
|
|
if (upb_inttable_lookupptr(&f->layouts, m, &v)) { |
|
UPB_ASSERT(upb_value_getptr(v)); |
|
return upb_value_getptr(v); |
|
} else { |
|
/* In case of circular dependency, layout has to be inserted first. */ |
|
upb_msglayout *l = upb_gmalloc(sizeof(*l)); |
|
upb_msgfactory *mutable_f = (void*)f; |
|
upb_inttable_insertptr(&mutable_f->layouts, m, upb_value_ptr(l)); |
|
UPB_ASSERT(l); |
|
if (!upb_msglayout_init(m, l, f)) { |
|
upb_msglayout_free(l); |
|
} |
|
return l; |
|
} |
|
}
|
|
|