Merge branch 'master' into fastest-table

Unfortunately this regresses the benchmark to ~2.25GB/s.
Optimizations forthcoming.
pull/13171/head
Joshua Haberman 4 years ago
commit 883f20d4dc
  1. 190
      upb/decode.c
  2. 61
      upb/decode.int.h
  3. 134
      upb/decode_fast.c

@ -142,7 +142,7 @@ typedef union {
bool bool_val;
uint32_t uint32_val;
uint64_t uint64_val;
upb_strview str_val;
uint32_t size;
} wireval;
static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
@ -197,41 +197,48 @@ static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t elem) {
return need_realloc;
}
typedef struct {
const char *ptr;
uint64_t val;
} decode_vret;
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;
static decode_vret decode_longvarint64(const char *ptr, uint64_t val) {
decode_vret ret = {NULL, 0};
uint64_t byte;
int i;
for (i = 1; i < 10; i++) {
byte = (uint8_t)ptr[i];
val += (byte - 1) << (i * 7);
if (!(byte & 0x80)) {
ret.ptr = ptr + i + 1;
ret.val = val;
return ret;
}
}
return ret;
}
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;
uint64_t *val) {
uint64_t byte = (uint8_t)*ptr;
if (UPB_LIKELY((byte & 0x80) == 0)) {
*val = byte;
return ptr + 1;
} else {
return decode_longvarint64(d, ptr, limit, val);
decode_vret res = decode_longvarint64(ptr, byte);
if (!res.ptr) decode_err(d);
*val = res.val;
return res.ptr;
}
}
UPB_FORCEINLINE
static const char *decode_varint32(upb_decstate *d, const char *ptr,
const char *limit, uint32_t *val) {
uint32_t *val) {
uint64_t u64;
ptr = decode_varint64(d, ptr, limit, &u64);
ptr = decode_varint64(d, ptr, &u64);
if (u64 > UINT32_MAX) decode_err(d);
*val = (uint32_t)u64;
return ptr;
@ -284,19 +291,46 @@ static upb_msg *decode_newsubmsg(upb_decstate *d, const upb_msglayout *layout,
return _upb_msg_new_inl(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) {
typedef struct {
bool ok;
const char *ptr;
} decode_doneret;
UPB_NOINLINE
const char *decode_isdonefallback(upb_decstate *d, const char *ptr,
int overrun) {
ptr = decode_isdonefallback_inl(d, ptr, overrun);
if (ptr == NULL) {
decode_err(d);
}
return ptr;
}
static const char *decode_readstr(upb_decstate *d, const char *ptr, int size,
upb_strview *str) {
if (d->alias) {
str->data = ptr;
} else {
char *data = upb_arena_malloc(&d->arena, size);
if (!data) decode_err(d);
memcpy(data, ptr, size);
str->data = data;
}
str->size = size;
return ptr + size;
}
static const char *decode_tosubmsg(upb_decstate *d, const char *ptr,
upb_msg *submsg, const upb_msglayout *layout,
const upb_msglayout_field *field, int size) {
const upb_msglayout *subl = layout->submsgs[field->submsg_index];
const char *saved_limit = d->limit;
int saved_delta = decode_pushlimit(d, ptr, size);
if (--d->depth < 0) decode_err(d);
d->limit = val.data + val.size;
d->fastlimit = UPB_MIN(d->limit, d->fastend);
decode_msg(d, val.data, submsg, subl);
d->limit = saved_limit;
d->fastlimit = UPB_MIN(d->limit, d->fastend);
ptr = decode_msg(d, ptr, submsg, subl);
decode_poplimit(d, saved_delta);
if (d->end_group != 0) decode_err(d);
d->depth++;
return ptr;
}
static const char *decode_group(upb_decstate *d, const char *ptr,
@ -345,15 +379,14 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
memcpy(mem, &val, 1 << op);
return ptr;
case OP_STRING:
decode_verifyutf8(d, val.str_val.data, val.str_val.size);
decode_verifyutf8(d, ptr, val.size);
/* Fallthrough. */
case OP_BYTES:
case OP_BYTES: {
/* Append bytes. */
mem =
UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(upb_strview), void);
upb_strview *str = (upb_strview*)_upb_array_ptr(arr) + arr->len;
arr->len++;
memcpy(mem, &val, sizeof(upb_strview));
return ptr;
return decode_readstr(d, ptr, val.size, str);
}
case OP_SUBMSG: {
/* Append submessage / group. */
upb_msg *submsg = decode_newsubmsg(d, layout, field);
@ -361,26 +394,25 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
submsg;
arr->len++;
if (UPB_UNLIKELY(field->descriptortype == UPB_DTYPE_GROUP)) {
ptr = decode_togroup(d, ptr, submsg, layout, field);
return decode_togroup(d, ptr, submsg, layout, field);
} else {
decode_tosubmsg(d, submsg, layout, field, val.str_val);
return decode_tosubmsg(d, ptr, submsg, layout, field, val.size);
}
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) {
size_t count = val.size >> lg2;
if ((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;
memcpy(mem, ptr, val.size); /* XXX: ptr boundary. */
return ptr + val.size;
}
case OP_VARPCK_LG2(0):
case OP_VARPCK_LG2(2):
@ -388,12 +420,11 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
/* 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;
int saved_limit = decode_pushlimit(d, ptr, val.size);
char *out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
while (ptr < end) {
while (!decode_isdone(d, &ptr)) {
wireval elem;
ptr = decode_varint64(d, ptr, end, &elem.uint64_val);
ptr = decode_varint64(d, ptr, &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);
@ -402,7 +433,7 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
memcpy(out, &elem, scale);
out += scale;
}
if (ptr != end) decode_err(d);
decode_poplimit(d, saved_limit);
return ptr;
}
default:
@ -410,9 +441,9 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
}
}
static void decode_tomap(upb_decstate *d, upb_msg *msg,
const upb_msglayout *layout,
const upb_msglayout_field *field, wireval val) {
static const char *decode_tomap(upb_decstate *d, const char *ptr, 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;
@ -440,10 +471,9 @@ static void decode_tomap(upb_decstate *d, upb_msg *msg,
ent.v.val = upb_value_ptr(_upb_msg_new(entry->submsgs[0], &d->arena));
}
decode_tosubmsg(d, &ent.k, layout, field, val.str_val);
/* Insert into map. */
ptr = decode_tosubmsg(d, ptr, &ent.k, layout, field, val.size);
_upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, &d->arena);
return ptr;
}
UPB_FORCEINLINE
@ -478,16 +508,15 @@ static const char *decode_tomsg(upb_decstate *d, const char *ptr, upb_msg *msg,
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);
ptr = decode_tosubmsg(d, ptr, submsg, layout, field, val.size);
}
break;
}
case OP_STRING:
decode_verifyutf8(d, val.str_val.data, val.str_val.size);
decode_verifyutf8(d, ptr, val.size);
/* Fallthrough. */
case OP_BYTES:
memcpy(mem, &val, sizeof(upb_strview));
break;
return decode_readstr(d, ptr, val.size, mem);
case OP_SCALAR_LG2(3):
memcpy(mem, &val, 8);
break;
@ -521,7 +550,7 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
int op;
decode_parseret ret;
ptr = decode_varint32(d, ptr, d->limit, &tag);
ptr = decode_varint32(d, ptr, &tag);
field_number = tag >> 3;
wire_type = tag & 7;
@ -529,12 +558,11 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
switch (wire_type) {
case UPB_WIRE_TYPE_VARINT:
ptr = decode_varint64(d, ptr, d->limit, &val.uint64_val);
ptr = decode_varint64(d, ptr, &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.uint32_val, ptr, 4);
val.uint32_val = _upb_be_swap32(val.uint32_val);
ptr += 4;
@ -542,7 +570,6 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
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.uint64_val, ptr, 8);
val.uint64_val = _upb_be_swap64(val.uint64_val);
ptr += 8;
@ -550,16 +577,12 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
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) {
ptr = decode_varint32(d, ptr, &val.size);
if (val.size >= INT32_MAX || ptr - d->end + val.size > d->limit) {
decode_err(d); /* Length overflow. */
}
val.str_val.data = ptr;
val.str_val.size = size;
ptr += size;
op = delim_ops[ndx];
break;
}
@ -585,7 +608,7 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
ptr = decode_toarray(d, ptr, msg, layout, field, val, op);
break;
case _UPB_LABEL_MAP:
decode_tomap(d, msg, layout, field, val);
ptr = decode_tomap(d, ptr, msg, layout, field, val);
break;
default:
ptr = decode_tomsg(d, ptr, msg, layout, field, val, op);
@ -599,6 +622,7 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
ptr = decode_group(d, ptr, NULL, NULL, field_number);
}
if (msg) {
if (wire_type == UPB_WIRE_TYPE_DELIMITED) ptr += val.size;
if (!_upb_msg_addunknown(msg, field_start, ptr - field_start,
&d->arena)) {
decode_err(d);
@ -618,7 +642,7 @@ const char *fastdecode_generic(upb_decstate *d, const char *ptr, upb_msg *msg,
decode_parseret ret;
*(uint32_t*)msg |= hasbits >> 16; /* Sync hasbits. */
(void)data;
if (ptr == d->limit) return ptr;
if (decode_isdone(d, &ptr)) return ptr;
ret = decode_field(d, ptr, msg, table);
if (ret.group_end) return ptr;
return fastdecode_dispatch(d, ret.ptr, msg, table, hasbits);
@ -630,13 +654,12 @@ static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
if (msg) {
ptr = fastdecode_dispatch(d, ptr, msg, layout, 0);
} else {
while (ptr < d->limit) {
while (!decode_isdone(d, &ptr)) {
decode_parseret ret = decode_field(d, ptr, msg, layout);
ptr = ret.ptr;
if (ret.group_end) return ptr;
}
}
if (ptr != d->limit) decode_err(d);
return ptr;
}
@ -645,11 +668,22 @@ bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l,
bool ok;
upb_decstate state;
if (size == 0) return true;
if (size == 0) {
return true;
} else if (size < 16) {
memset(&state.patch, 0, 32);
memcpy(&state.patch, buf, size);
buf = state.patch;
state.end = buf + size;
state.limit = 0;
state.alias = false;
} else {
state.end = buf + size - 16;
state.limit = 16;
state.alias = true;
}
state.limit = buf + size;
state.fastlimit = state.limit - 16;
state.fastend = state.fastlimit;
state.limit_ptr = state.end;
state.depth = 64;
state.end_group = 0;
state.arena.head = arena->head;

@ -15,12 +15,14 @@
#include "upb/port_def.inc"
typedef struct upb_decstate {
const char *limit; /* End of delimited region or end of buffer. */
const char *fastend; /* The end of the entire buffer - 16 */
const char *fastlimit; /* UPB_MIN(limit, fastend) */
upb_arena arena;
const char *end; /* Can read up to 16 bytes slop beyond this. */
const char *limit_ptr; /* = end + UPB_MIN(limit, 0) */
int limit; /* Submessage limit relative to end. */
int depth;
uint32_t end_group; /* Set to field number of END_GROUP tag, if any. */
bool alias;
char patch[32];
upb_arena arena;
jmp_buf err;
} upb_decstate;
@ -36,6 +38,57 @@ const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
* noreturn. */
const char *fastdecode_err(upb_decstate *d);
const char *decode_isdonefallback(upb_decstate *d, const char *ptr,
int overrun);
UPB_INLINE
const char *decode_isdonefallback_inl(upb_decstate *d, const char *ptr,
int overrun) {
if (overrun < d->limit) {
/* Need to copy remaining data into patch buffer. */
UPB_ASSERT(overrun < 16);
memset(d->patch + 16, 0, 16);
memcpy(d->patch, d->end, 16);
ptr = &d->patch[0] + overrun;
d->end = &d->patch[16];
d->limit -= 16;
d->limit_ptr = d->end + d->limit;
d->alias = false;
UPB_ASSERT(ptr < d->limit_ptr);
return ptr;
} else {
return NULL;
}
}
UPB_INLINE
bool decode_isdone(upb_decstate *d, const char **ptr) {
int overrun = *ptr - d->end;
if (UPB_LIKELY(*ptr < d->limit_ptr)) {
return false;
} else if (UPB_LIKELY(overrun == d->limit)) {
return true;
} else {
*ptr = decode_isdonefallback(d, *ptr, overrun);
return false;
}
}
UPB_INLINE
int decode_pushlimit(upb_decstate *d, const char *ptr, int size) {
int limit = size + (int)(ptr - d->end);
int delta = d->limit - limit;
d->limit = limit;
d->limit_ptr = d->end + UPB_MIN(0, limit);
return delta;
}
UPB_INLINE
void decode_poplimit(upb_decstate *d, int saved_delta) {
d->limit += saved_delta;
d->limit_ptr = d->end + UPB_MIN(0, d->limit);
}
#include "upb/port_undef.inc"
#endif /* UPB_DECODE_INT_H_ */

@ -72,19 +72,31 @@ static uint32_t fastdecode_loadtag(const char *ptr) {
return tag;
}
UPB_NOINLINE
static const char *fastdecode_isdonefallback(upb_decstate *d, const char *ptr,
upb_msg *msg,
const upb_msglayout *table,
uint64_t hasbits, int overrun) {
ptr = decode_isdonefallback_inl(d, ptr, overrun);
if (ptr == NULL) {
return fastdecode_err(d);
}
uint16_t tag = fastdecode_loadtag(ptr);
return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag);
}
UPB_FORCEINLINE
const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
const upb_msglayout *table, uint64_t hasbits) {
if (UPB_UNLIKELY(ptr >= d->fastlimit)) {
if (UPB_LIKELY(ptr == d->limit)) {
if (UPB_UNLIKELY(ptr >= d->limit_ptr)) {
int overrun = ptr - d->end;
if (UPB_LIKELY(overrun == d->limit)) {
// Parse is finished.
*(uint32_t*)msg |= hasbits >> 16; // Sync hasbits.
return ptr;
} else {
return fastdecode_isdonefallback(d, ptr, msg, table, hasbits, overrun);
}
// We are within 16 bytes of end-of-buffer, so we can't use fast parsing
// functions anymore (they will read up to 16b without bounds checks).
uint64_t data = 0;
RETURN_GENERIC("dispatch hit end\n");
}
// Read two bytes of tag data (for a one-byte tag, the high byte is junk).
@ -247,11 +259,26 @@ UPB_FORCEINLINE
static bool fastdecode_boundscheck(const char *ptr, size_t len,
const char *end) {
uintptr_t uptr = (uintptr_t)ptr;
uintptr_t uend = (uintptr_t)end;
uintptr_t uend = (uintptr_t)end + 16;
uintptr_t res = uptr + len;
return res < uptr || res > uend;
}
UPB_NOINLINE
static const char *fastdecode_copystring(struct upb_decstate *d,
size_t len, upb_msg *msg,
const upb_msglayout *table,
uint64_t hasbits, upb_strview *str) {
char *ptr = (char*)str->data;
char *data = upb_arena_malloc(&d->arena, len);
if (!data) {
return fastdecode_err(d);
}
memcpy(data, ptr, len);
str->data = data;
return fastdecode_dispatch(d, ptr + len, msg, table, hasbits);
}
UPB_FORCEINLINE
static const char *fastdecode_string(UPB_PARSE_PARAMS, int tagbytes,
upb_card card) {
@ -268,10 +295,13 @@ static const char *fastdecode_string(UPB_PARSE_PARAMS, int tagbytes,
str = ptr + tagbytes + 1;
dst->data = str;
dst->size = len;
if (UPB_UNLIKELY(fastdecode_boundscheck(str, len, d->limit))) {
if (UPB_UNLIKELY(fastdecode_boundscheck(str, len, d->limit_ptr))) {
dst->size = 0;
RETURN_GENERIC("string field len >1 byte\n");
}
if (!d->alias) {
return fastdecode_copystring(d, len, msg, table, hasbits, dst);
}
return fastdecode_dispatch(d, str + len, msg, table, hasbits);
}
@ -293,37 +323,6 @@ const char *upb_pos_2bt(UPB_PARSE_PARAMS) {
/* message fields *************************************************************/
UPB_NOINLINE
static const char *fastdecode_tosubmsg(upb_decstate *d, const char *ptr,
upb_msg *msg, const upb_msglayout *table,
uint64_t hasbits,
const char *saved_limit) {
size_t len = (uint8_t)ptr[-1];
if (UPB_UNLIKELY(len & 0x80)) {
int i;
for (i = 0; i < 3; i++) {
ptr++;
size_t byte = (uint8_t)ptr[-1];
len += (byte - 1) << (7 + 7 * i);
if (UPB_LIKELY((byte & 0x80) == 0)) goto done;
}
ptr++;
size_t byte = (uint8_t)ptr[-1];
// len is limited by 2gb not 4gb, hence 8 and not 16 as normally expected
// for a 32 bit varint.
if (UPB_UNLIKELY(byte >= 8)) return fastdecode_err(d);
len += (byte - 1) << 28;
}
done:
if (UPB_UNLIKELY(fastdecode_boundscheck(ptr, len, saved_limit))) {
return fastdecode_err(d);
}
d->limit = ptr + len;
d->fastlimit = UPB_MIN(d->limit, d->fastend);
return fastdecode_dispatch(d, ptr, msg, table, hasbits);
}
UPB_FORCEINLINE
static const char *fastdecode_submsg(UPB_PARSE_PARAMS, int tagbytes,
int msg_ceil_bytes, upb_card card) {
@ -348,9 +347,6 @@ static const char *fastdecode_submsg(UPB_PARSE_PARAMS, int tagbytes,
hasbits = 0;
}
const char *saved_limit = d->limit;
const char *saved_fastlimit = d->fastlimit;
again:
if (card == CARD_r) {
if (UPB_UNLIKELY(submsg == end)) {
@ -374,16 +370,37 @@ again:
}
ptr += tagbytes + 1;
ptr = fastdecode_tosubmsg(d, ptr, child, subl, 0, saved_limit);
if (UPB_UNLIKELY(ptr != d->limit || d->end_group != 0)) {
size_t len = (uint8_t)ptr[-1];
int saved_delta;
if (UPB_UNLIKELY(len & 0x80)) {
int i;
for (i = 0; i < 3; i++) {
ptr++;
size_t byte = (uint8_t)ptr[-1];
len += (byte - 1) << (7 + 7 * i);
if (UPB_LIKELY((byte & 0x80) == 0)) goto done;
}
ptr++;
size_t byte = (uint8_t)ptr[-1];
// len is limited by 2gb not 4gb, hence 8 and not 16 as normally expected
// for a 32 bit varint.
if (UPB_UNLIKELY(byte >= 8)) return fastdecode_err(d);
len += (byte - 1) << 28;
}
done:
if (ptr - d->end + (int)len > d->limit) {
return fastdecode_err(d);
}
saved_delta = decode_pushlimit(d, ptr, len);
ptr = fastdecode_dispatch(d, ptr, child, subl, 0);
decode_poplimit(d, saved_delta);
if (UPB_UNLIKELY(d->end_group != 0)) {
return fastdecode_err(d);
}
if (card == CARD_r) {
submsg++;
if (UPB_LIKELY(ptr < saved_fastlimit)) {
if (UPB_LIKELY(!decode_isdone(d, &ptr))) {
uint32_t tag = fastdecode_loadtag(ptr);
if (tagbytes == 1) {
if ((uint8_t)tag == (uint8_t)data) goto again;
@ -391,34 +408,17 @@ again:
if ((uint16_t)tag == (uint16_t)data) goto again;
}
arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
d->limit = saved_limit;
d->fastlimit = saved_fastlimit;
d->depth++;
return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag);
} else {
if (ptr == saved_limit) {
arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
d->limit = saved_limit;
d->fastlimit = saved_fastlimit;
d->depth++;
return ptr;
}
goto repeated_generic;
arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
d->depth++;
return ptr;
}
}
d->limit = saved_limit;
d->fastlimit = saved_fastlimit;
d->depth++;
return fastdecode_dispatch(d, ptr, msg, table, hasbits);
repeated_generic:
arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
d->limit = saved_limit;
d->fastlimit = saved_fastlimit;
d->depth++;
RETURN_GENERIC("repeated generic");
}
#define F(card, tagbytes, size_ceil, ceil_arg) \

Loading…
Cancel
Save