#include #include #include "upb/decode.h" #include "upb/upb.h" #include "upb/port_def.inc" /* Maps descriptor type -> upb field type. */ static const uint8_t desctype_to_fieldtype[] = { -1, /* invalid descriptor type */ UPB_TYPE_DOUBLE, /* DOUBLE */ UPB_TYPE_FLOAT, /* FLOAT */ UPB_TYPE_INT64, /* INT64 */ UPB_TYPE_UINT64, /* UINT64 */ UPB_TYPE_INT32, /* INT32 */ UPB_TYPE_UINT64, /* FIXED64 */ UPB_TYPE_UINT32, /* FIXED32 */ UPB_TYPE_BOOL, /* BOOL */ UPB_TYPE_STRING, /* STRING */ UPB_TYPE_MESSAGE, /* GROUP */ UPB_TYPE_MESSAGE, /* MESSAGE */ UPB_TYPE_BYTES, /* BYTES */ UPB_TYPE_UINT32, /* UINT32 */ UPB_TYPE_ENUM, /* ENUM */ UPB_TYPE_INT32, /* SFIXED32 */ UPB_TYPE_INT64, /* SFIXED64 */ UPB_TYPE_INT32, /* SINT32 */ UPB_TYPE_INT64, /* SINT64 */ }; /* Maps descriptor type -> upb map size. */ static const uint8_t desctype_to_mapsize[] = { -1, /* invalid descriptor type */ 8, /* DOUBLE */ 4, /* FLOAT */ 8, /* INT64 */ 8, /* UINT64 */ 4, /* INT32 */ 8, /* FIXED64 */ 4, /* FIXED32 */ 1, /* BOOL */ UPB_MAPTYPE_STRING, /* STRING */ sizeof(void *), /* GROUP */ sizeof(void *), /* MESSAGE */ UPB_MAPTYPE_STRING, /* BYTES */ 4, /* UINT32 */ 4, /* ENUM */ 4, /* SFIXED32 */ 8, /* SFIXED64 */ 4, /* SINT32 */ 8, /* SINT64 */ }; static const unsigned fixed32_ok = (1 << UPB_DTYPE_FLOAT) | (1 << UPB_DTYPE_FIXED32) | (1 << UPB_DTYPE_SFIXED32); static const unsigned fixed64_ok = (1 << UPB_DTYPE_DOUBLE) | (1 << UPB_DTYPE_FIXED64) | (1 << UPB_DTYPE_SFIXED64); /* Op: an action to be performed for a wire-type/field-type combination. */ #define OP_SCALAR_LG2(n) (n) #define OP_FIXPCK_LG2(n) (n + 4) #define OP_VARPCK_LG2(n) (n + 8) #define OP_STRING 4 #define OP_SUBMSG 5 static const int8_t varint_ops[19] = { -1, /* field not found */ -1, /* DOUBLE */ -1, /* FLOAT */ OP_SCALAR_LG2(3), /* INT64 */ OP_SCALAR_LG2(3), /* UINT64 */ OP_SCALAR_LG2(2), /* INT32 */ -1, /* FIXED64 */ -1, /* FIXED32 */ OP_SCALAR_LG2(0), /* BOOL */ -1, /* STRING */ -1, /* GROUP */ -1, /* MESSAGE */ -1, /* BYTES */ OP_SCALAR_LG2(2), /* UINT32 */ OP_SCALAR_LG2(2), /* ENUM */ -1, /* SFIXED32 */ -1, /* SFIXED64 */ OP_SCALAR_LG2(2), /* SINT32 */ OP_SCALAR_LG2(3), /* SINT64 */ }; static const int8_t delim_ops[37] = { /* For non-repeated field type. */ -1, /* field not found */ -1, /* DOUBLE */ -1, /* FLOAT */ -1, /* INT64 */ -1, /* UINT64 */ -1, /* INT32 */ -1, /* FIXED64 */ -1, /* FIXED32 */ -1, /* BOOL */ OP_STRING, /* STRING */ -1, /* GROUP */ OP_SUBMSG, /* MESSAGE */ OP_STRING, /* BYTES */ -1, /* UINT32 */ -1, /* ENUM */ -1, /* SFIXED32 */ -1, /* SFIXED64 */ -1, /* SINT32 */ -1, /* SINT64 */ /* For repeated field type. */ OP_FIXPCK_LG2(3), /* REPEATED DOUBLE */ OP_FIXPCK_LG2(2), /* REPEATED FLOAT */ OP_VARPCK_LG2(3), /* REPEATED INT64 */ OP_VARPCK_LG2(3), /* REPEATED UINT64 */ OP_VARPCK_LG2(2), /* REPEATED INT32 */ OP_FIXPCK_LG2(3), /* REPEATED FIXED64 */ OP_FIXPCK_LG2(2), /* REPEATED FIXED32 */ OP_VARPCK_LG2(0), /* REPEATED BOOL */ OP_STRING, /* REPEATED STRING */ OP_SUBMSG, /* REPEATED GROUP */ OP_SUBMSG, /* REPEATED MESSAGE */ OP_STRING, /* REPEATED BYTES */ OP_VARPCK_LG2(2), /* REPEATED UINT32 */ OP_VARPCK_LG2(2), /* REPEATED ENUM */ OP_FIXPCK_LG2(2), /* REPEATED SFIXED32 */ OP_FIXPCK_LG2(3), /* REPEATED SFIXED64 */ OP_VARPCK_LG2(2), /* REPEATED SINT32 */ OP_VARPCK_LG2(3), /* REPEATED SINT64 */ }; /* Data pertaining to the parse. */ typedef struct { const char *limit; /* End of delimited region or end of buffer. */ upb_arena *arena; int depth; uint32_t end_group; /* Set to field number of END_GROUP tag, if any. */ jmp_buf err; } upb_decstate; typedef union { bool bool_val; int32_t int32_val; int64_t int64_val; uint32_t uint32_val; uint64_t uint64_val; upb_strview str_val; } wireval; static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg, const upb_msglayout *layout); UPB_NORETURN static void decode_err(upb_decstate *d) { longjmp(d->err, 1); } static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t elem) { bool need_realloc = arr->size - arr->len < elem; if (need_realloc && !_upb_array_realloc(arr, arr->len + elem, d->arena)) { decode_err(d); } return need_realloc; } UPB_NOINLINE static const char *decode_longvarint64(upb_decstate *d, const char *ptr, const char *limit, uint64_t *val) { uint8_t byte; int bitpos = 0; uint64_t out = 0; do { if (bitpos >= 70 || ptr == limit) decode_err(d); byte = *ptr; out |= (uint64_t)(byte & 0x7F) << bitpos; ptr++; bitpos += 7; } while (byte & 0x80); *val = out; return ptr; } UPB_FORCEINLINE static const char *decode_varint64(upb_decstate *d, const char *ptr, const char *limit, uint64_t *val) { if (UPB_LIKELY(ptr < limit && (*ptr & 0x80) == 0)) { *val = (uint8_t)*ptr; return ptr + 1; } else { return decode_longvarint64(d, ptr, limit, val); } } static const char *decode_varint32(upb_decstate *d, const char *ptr, const char *limit, uint32_t *val) { uint64_t u64; ptr = decode_varint64(d, ptr, limit, &u64); if (u64 > UINT32_MAX) decode_err(d); *val = (uint32_t)u64; return ptr; } static void decode_munge(int type, wireval *val) { switch (type) { case UPB_DESCRIPTOR_TYPE_BOOL: val->bool_val = val->uint64_val != 0; break; case UPB_DESCRIPTOR_TYPE_SINT32: { uint32_t n = val->uint32_val; val->int32_val = (n >> 1) ^ -(int32_t)(n & 1); break; } case UPB_DESCRIPTOR_TYPE_SINT64: { uint64_t n = val->uint64_val; val->int64_val = (n >> 1) ^ -(int64_t)(n & 1); break; } } } static const upb_msglayout_field *upb_find_field(const upb_msglayout *l, uint32_t field_number) { static upb_msglayout_field none = {0}; /* Lots of optimization opportunities here. */ int i; if (l == NULL) return &none; for (i = 0; i < l->field_count; i++) { if (l->fields[i].number == field_number) { return &l->fields[i]; } } return &none; /* Unknown field. */ } static upb_msg *decode_newsubmsg(upb_decstate *d, const upb_msglayout *layout, const upb_msglayout_field *field) { const upb_msglayout *subl = layout->submsgs[field->submsg_index]; return _upb_msg_new(subl, d->arena); } static void decode_tosubmsg(upb_decstate *d, upb_msg *submsg, const upb_msglayout *layout, const upb_msglayout_field *field, upb_strview val) { const upb_msglayout *subl = layout->submsgs[field->submsg_index]; const char *saved_limit = d->limit; if (--d->depth < 0) decode_err(d); d->limit = val.data + val.size; decode_msg(d, val.data, submsg, subl); d->limit = saved_limit; if (d->end_group != 0) decode_err(d); d->depth++; } static const char *decode_group(upb_decstate *d, const char *ptr, upb_msg *submsg, const upb_msglayout *subl, uint32_t number) { if (--d->depth < 0) decode_err(d); ptr = decode_msg(d, ptr, submsg, subl); if (d->end_group != number) decode_err(d); d->end_group = 0; d->depth++; return ptr; } static const char *decode_togroup(upb_decstate *d, const char *ptr, upb_msg *submsg, const upb_msglayout *layout, const upb_msglayout_field *field) { const upb_msglayout *subl = layout->submsgs[field->submsg_index]; return decode_group(d, ptr, submsg, subl, field->number); } static const char *decode_toarray(upb_decstate *d, const char *ptr, upb_msg *msg, const upb_msglayout *layout, const upb_msglayout_field *field, wireval val, int op) { upb_array **arrp = UPB_PTR_AT(msg, field->offset, void); upb_array *arr = *arrp; void *mem; if (!arr) { upb_fieldtype_t type = desctype_to_fieldtype[field->descriptortype]; arr = _upb_array_new(d->arena, type); if (!arr) decode_err(d); *arrp = arr; } decode_reserve(d, arr, 1); switch (op) { case OP_SCALAR_LG2(0): case OP_SCALAR_LG2(2): case OP_SCALAR_LG2(3): /* Append scalar value. */ mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << op, void); arr->len++; memcpy(mem, &val, 1 << op); return ptr; case OP_STRING: /* Append string. */ mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(upb_strview), void); arr->len++; memcpy(mem, &val, sizeof(upb_strview)); return ptr; case OP_SUBMSG: { /* Append submessage / group. */ upb_msg *submsg = decode_newsubmsg(d, layout, field); *UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(void *), upb_msg *) = submsg; arr->len++; if (UPB_UNLIKELY(field->descriptortype == UPB_DTYPE_GROUP)) { ptr = decode_togroup(d, ptr, submsg, layout, field); } else { decode_tosubmsg(d, submsg, layout, field, val.str_val); } return ptr; } case OP_FIXPCK_LG2(2): case OP_FIXPCK_LG2(3): { /* Fixed packed. */ int lg2 = op - OP_FIXPCK_LG2(0); int mask = (1 << lg2) - 1; size_t count = val.str_val.size >> lg2; if ((val.str_val.size & mask) != 0) { decode_err(d); /* Length isn't a round multiple of elem size. */ } decode_reserve(d, arr, count); mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void); arr->len += count; memcpy(mem, val.str_val.data, val.str_val.size); return ptr; } case OP_VARPCK_LG2(0): case OP_VARPCK_LG2(2): case OP_VARPCK_LG2(3): { /* Varint packed. */ int lg2 = op - OP_VARPCK_LG2(0); int scale = 1 << lg2; const char *ptr = val.str_val.data; const char *end = ptr + val.str_val.size; char *out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void); while (ptr < end) { wireval elem; ptr = decode_varint64(d, ptr, end, &elem.uint64_val); decode_munge(field->descriptortype, &elem); if (decode_reserve(d, arr, 1)) { out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void); } arr->len++; memcpy(out, &elem, scale); out += scale; } if (ptr != end) decode_err(d); return ptr; } default: UPB_UNREACHABLE(); } } static void decode_tomap(upb_decstate *d, upb_msg *msg, const upb_msglayout *layout, const upb_msglayout_field *field, wireval val) { upb_map **map_p = UPB_PTR_AT(msg, field->offset, upb_map *); upb_map *map = *map_p; upb_map_entry ent; const upb_msglayout *entry = layout->submsgs[field->submsg_index]; if (!map) { /* Lazily create map. */ const upb_msglayout *entry = layout->submsgs[field->submsg_index]; const upb_msglayout_field *key_field = &entry->fields[0]; const upb_msglayout_field *val_field = &entry->fields[1]; char key_size = desctype_to_mapsize[key_field->descriptortype]; char val_size = desctype_to_mapsize[val_field->descriptortype]; UPB_ASSERT(key_field->offset == 0); UPB_ASSERT(val_field->offset == sizeof(upb_strview)); map = _upb_map_new(d->arena, key_size, val_size); *map_p = map; } /* Parse map entry. */ memset(&ent, 0, sizeof(ent)); if (entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE || entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_GROUP) { /* Create proactively to handle the case where it doesn't appear. */ ent.v.val.val = (uint64_t)_upb_msg_new(entry->submsgs[0], d->arena); } decode_tosubmsg(d, &ent.k, layout, field, val.str_val); /* Insert into map. */ _upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, d->arena); } static const char *decode_tomsg(upb_decstate *d, const char *ptr, upb_msg *msg, const upb_msglayout *layout, const upb_msglayout_field *field, wireval val, int op) { void *mem = UPB_PTR_AT(msg, field->offset, void); int type = field->descriptortype; /* Set presence if necessary. */ if (field->presence < 0) { /* Oneof case */ uint32_t *oneof_case = _upb_oneofcase_field(msg, field); if (op == OP_SUBMSG && *oneof_case != field->number) { memset(mem, 0, sizeof(void*)); } *oneof_case = field->number; } else if (field->presence > 0) { _upb_sethas_field(msg, field); } /* Store into message. */ switch (op) { case OP_SUBMSG: { upb_msg **submsgp = mem; upb_msg *submsg = *submsgp; if (!submsg) { submsg = decode_newsubmsg(d, layout, field); *submsgp = submsg; } if (UPB_UNLIKELY(type == UPB_DTYPE_GROUP)) { ptr = decode_togroup(d, ptr, submsg, layout, field); } else { decode_tosubmsg(d, submsg, layout, field, val.str_val); } break; } case OP_STRING: memcpy(mem, &val, sizeof(upb_strview)); break; case OP_SCALAR_LG2(3): memcpy(mem, &val, 8); break; case OP_SCALAR_LG2(2): memcpy(mem, &val, 4); break; case OP_SCALAR_LG2(0): memcpy(mem, &val, 1); break; default: UPB_UNREACHABLE(); } return ptr; } static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg, const upb_msglayout *layout) { while (ptr < d->limit) { uint32_t tag; const upb_msglayout_field *field; int field_number; int wire_type; const char *field_start = ptr; wireval val; int op; ptr = decode_varint32(d, ptr, d->limit, &tag); field_number = tag >> 3; wire_type = tag & 7; field = upb_find_field(layout, field_number); switch (wire_type) { case UPB_WIRE_TYPE_VARINT: ptr = decode_varint64(d, ptr, d->limit, &val.uint64_val); op = varint_ops[field->descriptortype]; decode_munge(field->descriptortype, &val); break; case UPB_WIRE_TYPE_32BIT: if (d->limit - ptr < 4) decode_err(d); memcpy(&val, ptr, 4); ptr += 4; op = OP_SCALAR_LG2(2); if (((1 << field->descriptortype) & fixed32_ok) == 0) goto unknown; break; case UPB_WIRE_TYPE_64BIT: if (d->limit - ptr < 8) decode_err(d); memcpy(&val, ptr, 8); ptr += 8; op = OP_SCALAR_LG2(3); if (((1 << field->descriptortype) & fixed64_ok) == 0) goto unknown; break; case UPB_WIRE_TYPE_DELIMITED: { uint32_t size; int ndx = field->descriptortype; if (_upb_isrepeated(field)) ndx += 18; ptr = decode_varint32(d, ptr, d->limit, &size); if (size >= INT32_MAX || (size_t)(d->limit - ptr) < size) { decode_err(d); /* Length overflow. */ } val.str_val.data = ptr; val.str_val.size = size; ptr += size; op = delim_ops[ndx]; break; } case UPB_WIRE_TYPE_START_GROUP: val.int32_val = field_number; op = OP_SUBMSG; if (field->descriptortype != UPB_DTYPE_GROUP) goto unknown; break; case UPB_WIRE_TYPE_END_GROUP: d->end_group = field_number; return ptr; default: decode_err(d); } if (op >= 0) { /* Parse, using op for dispatch. */ switch (field->label) { case UPB_LABEL_REPEATED: case _UPB_LABEL_PACKED: ptr = decode_toarray(d, ptr, msg, layout, field, val, op); break; case _UPB_LABEL_MAP: decode_tomap(d, msg, layout, field, val); break; default: ptr = decode_tomsg(d, ptr, msg, layout, field, val, op); break; } } else { unknown: /* Skip unknown field. */ if (field_number == 0) decode_err(d); if (wire_type == UPB_WIRE_TYPE_START_GROUP) { ptr = decode_group(d, ptr, NULL, NULL, field_number); } if (msg) { if (!_upb_msg_addunknown(msg, field_start, ptr - field_start, d->arena)) { decode_err(d); } } } } if (ptr != d->limit) decode_err(d); return ptr; } bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l, upb_arena *arena) { upb_decstate state; state.limit = buf + size; state.arena = arena; state.depth = 64; state.end_group = 0; if (setjmp(state.err)) return false; if (size == 0) return true; decode_msg(&state, buf, msg, l); return state.end_group == 0; } #undef OP_SCALAR_LG2 #undef OP_FIXPCK_LG2 #undef OP_VARPCK_LG2 #undef OP_STRING #undef OP_SUBMSG