/** * lupb_map_typecheck() * * Checks that the lupb_map at index |narg| can be safely assigned to the * field |f| of the message at index |msg|. If so, returns a upb_msgval for * this map. Otherwise, raises a Lua error. */ static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg, const upb_fielddef *f) { lupb_map *lmap = lupb_map_check(L, narg); upb_map *map = lmap->map; const upb_msgdef *entry = upb_fielddef_msgsubdef(f); const upb_fielddef *key_field = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY); const upb_fielddef *value_field = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE); UPB_ASSERT(entry && key_field && value_field); if (upb_map_keytype(map) != upb_fielddef_type(key_field)) { luaL_error(L, "Map key type invalid"); } if (upb_map_valuetype(map) != upb_fielddef_type(value_field)) { luaL_error(L, "Map had incorrect value type (expected: %s, got: %s)", upb_fielddef_type(value_field), upb_map_valuetype(map)); } if (upb_map_valuetype(map) == UPB_TYPE_MESSAGE) { lupb_msgclass_typecheck( L, lupb_msg_msgclassfor(L, msg, upb_fielddef_msgsubdef(value_field)), lmap->value_lmsgclass); } return upb_msgval_map(map); }
// Creates a handlerdata that contains offset and submessage type information. static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, const upb_fielddef* f) { submsg_handlerdata_t *hd = ALLOC(submsg_handlerdata_t); hd->ofs = ofs; hd->md = upb_fielddef_msgsubdef(f); upb_handlers_addcleanup(h, hd, free); return hd; }
bool is_map_field(const upb_fielddef* field) { if (upb_fielddef_label(field) != UPB_LABEL_REPEATED || upb_fielddef_type(field) != UPB_TYPE_MESSAGE) { return false; } const upb_msgdef* subdef = upb_fielddef_msgsubdef(field); return upb_msgdef_mapentry(subdef); }
/** * lupb_msgclass_getsubmsgclass() * * Given a MessageClass at index |narg| and the submessage field |f|, returns * the message class for this field. * * Currently we do a hash table lookup for this. If we wanted we could try to * optimize this by caching these pointers in our msgclass, in an array indexed * by field index. We would still need to fall back to calling msgclassfor(), * unless we wanted to eagerly create message classes for all submessages. But * for big schemas that might be a lot of things to build, and we might end up * not using most of them. */ static const lupb_msgclass *lupb_msgclass_getsubmsgclass(lua_State *L, int narg, const upb_fielddef *f) { if (upb_fielddef_type(f) != UPB_TYPE_MESSAGE) { return NULL; } return lupb_msgclass_msgclassfor(L, narg, upb_fielddef_msgsubdef(f)); }
static void onmreg(const void *c, upb_handlers *h) { const upb_msgdef *m = upb_handlers_msgdef(h); upb_msg_field_iter i; UPB_UNUSED(c); upb_handlers_setstartmsg(h, textprinter_startmsg, NULL); upb_handlers_setendmsg(h, textprinter_endmsg, NULL); for(upb_msg_field_begin(&i, m); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, f); switch (upb_fielddef_type(f)) { case UPB_TYPE_INT32: upb_handlers_setint32(h, f, textprinter_putint32, &attr); break; case UPB_TYPE_INT64: upb_handlers_setint64(h, f, textprinter_putint64, &attr); break; case UPB_TYPE_UINT32: upb_handlers_setuint32(h, f, textprinter_putuint32, &attr); break; case UPB_TYPE_UINT64: upb_handlers_setuint64(h, f, textprinter_putuint64, &attr); break; case UPB_TYPE_FLOAT: upb_handlers_setfloat(h, f, textprinter_putfloat, &attr); break; case UPB_TYPE_DOUBLE: upb_handlers_setdouble(h, f, textprinter_putdouble, &attr); break; case UPB_TYPE_BOOL: upb_handlers_setbool(h, f, textprinter_putbool, &attr); break; case UPB_TYPE_STRING: case UPB_TYPE_BYTES: upb_handlers_setstartstr(h, f, textprinter_startstr, &attr); upb_handlers_setstring(h, f, textprinter_putstr, &attr); upb_handlers_setendstr(h, f, textprinter_endstr, &attr); break; case UPB_TYPE_MESSAGE: { const char *name = upb_fielddef_istagdelim(f) ? shortname(upb_msgdef_fullname(upb_fielddef_msgsubdef(f))) : upb_fielddef_name(f); upb_handlerattr_sethandlerdata(&attr, name); upb_handlers_setstartsubmsg(h, f, textprinter_startsubmsg, &attr); upb_handlers_setendsubmsg(h, f, textprinter_endsubmsg, &attr); break; } case UPB_TYPE_ENUM: upb_handlers_setint32(h, f, textprinter_putenum, &attr); break; } } }
const upb_msgdef* tryget_map_entry_msgdef(const upb_fielddef* field) { const upb_msgdef* subdef; if (upb_fielddef_label(field) != UPB_LABEL_REPEATED || upb_fielddef_type(field) != UPB_TYPE_MESSAGE) { return NULL; } subdef = upb_fielddef_msgsubdef(field); return upb_msgdef_mapentry(subdef) ? subdef : NULL; }
// Adds handlers to a map field. static void add_handlers_for_mapfield(upb_handlers* h, const upb_fielddef* fielddef, size_t offset, Descriptor* desc) { const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef); map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef, desc); upb_handlers_addcleanup(h, hd, free); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, hd); upb_handlers_setstartsubmsg(h, fielddef, startmapentry_handler, &attr); upb_handlerattr_uninit(&attr); }
static const void *newoneofhandlerdata(upb_handlers *h, uint32_t ofs, uint32_t case_ofs, const upb_fielddef *f) { oneof_handlerdata_t *hd = ALLOC(oneof_handlerdata_t); hd->ofs = ofs; hd->case_ofs = case_ofs; // We reuse the field tag number as a oneof union discriminant tag. Note that // we don't expose these numbers to the user, so the only requirement is that // we have some unique ID for each union case/possibility. The field tag // numbers are already present and are easy to use so there's no reason to // create a separate ID space. In addition, using the field tag number here // lets us easily look up the field in the oneof accessor. hd->oneof_case_num = upb_fielddef_number(f); if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) { hd->md = upb_fielddef_msgsubdef(f); } else { hd->md = NULL; } upb_handlers_addcleanup(h, hd, free); return hd; }
const upb_fielddef* map_field_value(const upb_fielddef* field) { assert(is_map_field(field)); const upb_msgdef* subdef = upb_fielddef_msgsubdef(field); return map_entry_value(subdef); }
bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { // TODO: verify we have a transitive closure. for (int i = 0; i < n; i++) { upb_handlers *h = handlers[i]; if (!upb_ok(&h->status_)) { upb_status_seterrf(s, "handlers for message %s had error status: %s", upb_msgdef_fullname(upb_handlers_msgdef(h)), upb_status_errmsg(&h->status_)); return false; } // Check that there are no closure mismatches due to missing Start* handlers // or subhandlers with different type-level types. upb_msg_iter j; for(upb_msg_begin(&j, h->msg); !upb_msg_done(&j); upb_msg_next(&j)) { const upb_fielddef *f = upb_msg_iter_field(&j); if (upb_fielddef_isseq(f)) { if (!checkstart(h, f, UPB_HANDLER_STARTSEQ, s)) return false; } if (upb_fielddef_isstring(f)) { if (!checkstart(h, f, UPB_HANDLER_STARTSTR, s)) return false; } if (upb_fielddef_issubmsg(f)) { bool hashandler = false; if (upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_STARTSUBMSG)) || upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_ENDSUBMSG))) { hashandler = true; } if (upb_fielddef_isseq(f) && (upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_STARTSEQ)) || upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_ENDSEQ)))) { hashandler = true; } if (hashandler && !upb_handlers_getsubhandlers(h, f)) { // For now we add an empty subhandlers in this case. It makes the // decoder code generator simpler, because it only has to handle two // cases (submessage has handlers or not) as opposed to three // (submessage has handlers in enclosing message but no subhandlers). // // This makes parsing less efficient in the case that we want to // notice a submessage but skip its contents (like if we're testing // for submessage presence or counting the number of repeated // submessages). In this case we will end up parsing the submessage // field by field and throwing away the results for each, instead of // skipping the whole delimited thing at once. If this is an issue we // can revisit it, but do remember that this only arises when you have // handlers (startseq/startsubmsg/endsubmsg/endseq) set for the // submessage but no subhandlers. The uses cases for this are // limited. upb_handlers *sub = upb_handlers_new(upb_fielddef_msgsubdef(f), &sub); upb_handlers_setsubhandlers(h, f, sub); upb_handlers_unref(sub, &sub); } // TODO(haberman): check type of submessage. // This is slightly tricky; also consider whether we should check that // they match at setsubhandlers time. } } } if (!upb_refcounted_freeze((upb_refcounted*const*)handlers, n, s, UPB_MAX_HANDLER_DEPTH)) { return false; } return true; }
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; }