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. 186
      upb/decode.c
  2. 61
      upb/decode.int.h
  3. 128
      upb/decode_fast.c

@ -142,7 +142,7 @@ typedef union {
bool bool_val; bool bool_val;
uint32_t uint32_val; uint32_t uint32_val;
uint64_t uint64_val; uint64_t uint64_val;
upb_strview str_val; uint32_t size;
} wireval; } wireval;
static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg, 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; return need_realloc;
} }
typedef struct {
const char *ptr;
uint64_t val;
} decode_vret;
UPB_NOINLINE UPB_NOINLINE
static const char *decode_longvarint64(upb_decstate *d, const char *ptr, static decode_vret decode_longvarint64(const char *ptr, uint64_t val) {
const char *limit, uint64_t *val) { decode_vret ret = {NULL, 0};
uint8_t byte; uint64_t byte;
int bitpos = 0; int i;
uint64_t out = 0; for (i = 1; i < 10; i++) {
byte = (uint8_t)ptr[i];
do { val += (byte - 1) << (i * 7);
if (bitpos >= 70 || ptr == limit) decode_err(d); if (!(byte & 0x80)) {
byte = *ptr; ret.ptr = ptr + i + 1;
out |= (uint64_t)(byte & 0x7F) << bitpos; ret.val = val;
ptr++; return ret;
bitpos += 7; }
} while (byte & 0x80); }
return ret;
*val = out;
return ptr;
} }
UPB_FORCEINLINE UPB_FORCEINLINE
static const char *decode_varint64(upb_decstate *d, const char *ptr, static const char *decode_varint64(upb_decstate *d, const char *ptr,
const char *limit, uint64_t *val) { uint64_t *val) {
if (UPB_LIKELY(ptr < limit && (*ptr & 0x80) == 0)) { uint64_t byte = (uint8_t)*ptr;
*val = (uint8_t)*ptr; if (UPB_LIKELY((byte & 0x80) == 0)) {
*val = byte;
return ptr + 1; return ptr + 1;
} else { } 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 UPB_FORCEINLINE
static const char *decode_varint32(upb_decstate *d, const char *ptr, static const char *decode_varint32(upb_decstate *d, const char *ptr,
const char *limit, uint32_t *val) { uint32_t *val) {
uint64_t u64; uint64_t u64;
ptr = decode_varint64(d, ptr, limit, &u64); ptr = decode_varint64(d, ptr, &u64);
if (u64 > UINT32_MAX) decode_err(d); if (u64 > UINT32_MAX) decode_err(d);
*val = (uint32_t)u64; *val = (uint32_t)u64;
return ptr; 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); return _upb_msg_new_inl(subl, &d->arena);
} }
static void decode_tosubmsg(upb_decstate *d, upb_msg *submsg, typedef struct {
const upb_msglayout *layout, bool ok;
const upb_msglayout_field *field, upb_strview val) { 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 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); if (--d->depth < 0) decode_err(d);
d->limit = val.data + val.size; ptr = decode_msg(d, ptr, submsg, subl);
d->fastlimit = UPB_MIN(d->limit, d->fastend); decode_poplimit(d, saved_delta);
decode_msg(d, val.data, submsg, subl);
d->limit = saved_limit;
d->fastlimit = UPB_MIN(d->limit, d->fastend);
if (d->end_group != 0) decode_err(d); if (d->end_group != 0) decode_err(d);
d->depth++; d->depth++;
return ptr;
} }
static const char *decode_group(upb_decstate *d, const char *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); memcpy(mem, &val, 1 << op);
return ptr; return ptr;
case OP_STRING: case OP_STRING:
decode_verifyutf8(d, val.str_val.data, val.str_val.size); decode_verifyutf8(d, ptr, val.size);
/* Fallthrough. */ /* Fallthrough. */
case OP_BYTES: case OP_BYTES: {
/* Append bytes. */ /* Append bytes. */
mem = upb_strview *str = (upb_strview*)_upb_array_ptr(arr) + arr->len;
UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(upb_strview), void);
arr->len++; arr->len++;
memcpy(mem, &val, sizeof(upb_strview)); return decode_readstr(d, ptr, val.size, str);
return ptr; }
case OP_SUBMSG: { case OP_SUBMSG: {
/* Append submessage / group. */ /* Append submessage / group. */
upb_msg *submsg = decode_newsubmsg(d, layout, field); upb_msg *submsg = decode_newsubmsg(d, layout, field);
@ -361,26 +394,25 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
submsg; submsg;
arr->len++; arr->len++;
if (UPB_UNLIKELY(field->descriptortype == UPB_DTYPE_GROUP)) { 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 { } 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(2):
case OP_FIXPCK_LG2(3): { case OP_FIXPCK_LG2(3): {
/* Fixed packed. */ /* Fixed packed. */
int lg2 = op - OP_FIXPCK_LG2(0); int lg2 = op - OP_FIXPCK_LG2(0);
int mask = (1 << lg2) - 1; int mask = (1 << lg2) - 1;
size_t count = val.str_val.size >> lg2; size_t count = val.size >> lg2;
if ((val.str_val.size & mask) != 0) { if ((val.size & mask) != 0) {
decode_err(d); /* Length isn't a round multiple of elem size. */ decode_err(d); /* Length isn't a round multiple of elem size. */
} }
decode_reserve(d, arr, count); decode_reserve(d, arr, count);
mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void); mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
arr->len += count; arr->len += count;
memcpy(mem, val.str_val.data, val.str_val.size); memcpy(mem, ptr, val.size); /* XXX: ptr boundary. */
return ptr; return ptr + val.size;
} }
case OP_VARPCK_LG2(0): case OP_VARPCK_LG2(0):
case OP_VARPCK_LG2(2): case OP_VARPCK_LG2(2):
@ -388,12 +420,11 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
/* Varint packed. */ /* Varint packed. */
int lg2 = op - OP_VARPCK_LG2(0); int lg2 = op - OP_VARPCK_LG2(0);
int scale = 1 << lg2; int scale = 1 << lg2;
const char *ptr = val.str_val.data; int saved_limit = decode_pushlimit(d, ptr, val.size);
const char *end = ptr + val.str_val.size;
char *out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void); char *out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
while (ptr < end) { while (!decode_isdone(d, &ptr)) {
wireval elem; wireval elem;
ptr = decode_varint64(d, ptr, end, &elem.uint64_val); ptr = decode_varint64(d, ptr, &elem.uint64_val);
decode_munge(field->descriptortype, &elem); decode_munge(field->descriptortype, &elem);
if (decode_reserve(d, arr, 1)) { if (decode_reserve(d, arr, 1)) {
out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void); 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); memcpy(out, &elem, scale);
out += scale; out += scale;
} }
if (ptr != end) decode_err(d); decode_poplimit(d, saved_limit);
return ptr; return ptr;
} }
default: default:
@ -410,7 +441,7 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
} }
} }
static void decode_tomap(upb_decstate *d, upb_msg *msg, static const char *decode_tomap(upb_decstate *d, const char *ptr, upb_msg *msg,
const upb_msglayout *layout, const upb_msglayout *layout,
const upb_msglayout_field *field, wireval val) { const upb_msglayout_field *field, wireval val) {
upb_map **map_p = UPB_PTR_AT(msg, field->offset, upb_map *); upb_map **map_p = UPB_PTR_AT(msg, field->offset, upb_map *);
@ -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)); ent.v.val = upb_value_ptr(_upb_msg_new(entry->submsgs[0], &d->arena));
} }
decode_tosubmsg(d, &ent.k, layout, field, val.str_val); ptr = decode_tosubmsg(d, ptr, &ent.k, layout, field, val.size);
/* Insert into map. */
_upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, &d->arena); _upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, &d->arena);
return ptr;
} }
UPB_FORCEINLINE 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)) { if (UPB_UNLIKELY(type == UPB_DTYPE_GROUP)) {
ptr = decode_togroup(d, ptr, submsg, layout, field); ptr = decode_togroup(d, ptr, submsg, layout, field);
} else { } else {
decode_tosubmsg(d, submsg, layout, field, val.str_val); ptr = decode_tosubmsg(d, ptr, submsg, layout, field, val.size);
} }
break; break;
} }
case OP_STRING: case OP_STRING:
decode_verifyutf8(d, val.str_val.data, val.str_val.size); decode_verifyutf8(d, ptr, val.size);
/* Fallthrough. */ /* Fallthrough. */
case OP_BYTES: case OP_BYTES:
memcpy(mem, &val, sizeof(upb_strview)); return decode_readstr(d, ptr, val.size, mem);
break;
case OP_SCALAR_LG2(3): case OP_SCALAR_LG2(3):
memcpy(mem, &val, 8); memcpy(mem, &val, 8);
break; break;
@ -521,7 +550,7 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
int op; int op;
decode_parseret ret; decode_parseret ret;
ptr = decode_varint32(d, ptr, d->limit, &tag); ptr = decode_varint32(d, ptr, &tag);
field_number = tag >> 3; field_number = tag >> 3;
wire_type = tag & 7; wire_type = tag & 7;
@ -529,12 +558,11 @@ static decode_parseret decode_field(upb_decstate *d, const char *ptr,
switch (wire_type) { switch (wire_type) {
case UPB_WIRE_TYPE_VARINT: 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]; op = varint_ops[field->descriptortype];
decode_munge(field->descriptortype, &val); decode_munge(field->descriptortype, &val);
break; break;
case UPB_WIRE_TYPE_32BIT: case UPB_WIRE_TYPE_32BIT:
if (d->limit - ptr < 4) decode_err(d);
memcpy(&val.uint32_val, ptr, 4); memcpy(&val.uint32_val, ptr, 4);
val.uint32_val = _upb_be_swap32(val.uint32_val); val.uint32_val = _upb_be_swap32(val.uint32_val);
ptr += 4; 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; if (((1 << field->descriptortype) & fixed32_ok) == 0) goto unknown;
break; break;
case UPB_WIRE_TYPE_64BIT: case UPB_WIRE_TYPE_64BIT:
if (d->limit - ptr < 8) decode_err(d);
memcpy(&val.uint64_val, ptr, 8); memcpy(&val.uint64_val, ptr, 8);
val.uint64_val = _upb_be_swap64(val.uint64_val); val.uint64_val = _upb_be_swap64(val.uint64_val);
ptr += 8; 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; if (((1 << field->descriptortype) & fixed64_ok) == 0) goto unknown;
break; break;
case UPB_WIRE_TYPE_DELIMITED: { case UPB_WIRE_TYPE_DELIMITED: {
uint32_t size;
int ndx = field->descriptortype; int ndx = field->descriptortype;
if (_upb_isrepeated(field)) ndx += 18; if (_upb_isrepeated(field)) ndx += 18;
ptr = decode_varint32(d, ptr, d->limit, &size); ptr = decode_varint32(d, ptr, &val.size);
if (size >= INT32_MAX || (size_t)(d->limit - ptr) < size) { if (val.size >= INT32_MAX || ptr - d->end + val.size > d->limit) {
decode_err(d); /* Length overflow. */ decode_err(d); /* Length overflow. */
} }
val.str_val.data = ptr;
val.str_val.size = size;
ptr += size;
op = delim_ops[ndx]; op = delim_ops[ndx];
break; 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); ptr = decode_toarray(d, ptr, msg, layout, field, val, op);
break; break;
case _UPB_LABEL_MAP: case _UPB_LABEL_MAP:
decode_tomap(d, msg, layout, field, val); ptr = decode_tomap(d, ptr, msg, layout, field, val);
break; break;
default: default:
ptr = decode_tomsg(d, ptr, msg, layout, field, val, op); 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); ptr = decode_group(d, ptr, NULL, NULL, field_number);
} }
if (msg) { if (msg) {
if (wire_type == UPB_WIRE_TYPE_DELIMITED) ptr += val.size;
if (!_upb_msg_addunknown(msg, field_start, ptr - field_start, if (!_upb_msg_addunknown(msg, field_start, ptr - field_start,
&d->arena)) { &d->arena)) {
decode_err(d); decode_err(d);
@ -618,7 +642,7 @@ const char *fastdecode_generic(upb_decstate *d, const char *ptr, upb_msg *msg,
decode_parseret ret; decode_parseret ret;
*(uint32_t*)msg |= hasbits >> 16; /* Sync hasbits. */ *(uint32_t*)msg |= hasbits >> 16; /* Sync hasbits. */
(void)data; (void)data;
if (ptr == d->limit) return ptr; if (decode_isdone(d, &ptr)) return ptr;
ret = decode_field(d, ptr, msg, table); ret = decode_field(d, ptr, msg, table);
if (ret.group_end) return ptr; if (ret.group_end) return ptr;
return fastdecode_dispatch(d, ret.ptr, msg, table, hasbits); 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) { if (msg) {
ptr = fastdecode_dispatch(d, ptr, msg, layout, 0); ptr = fastdecode_dispatch(d, ptr, msg, layout, 0);
} else { } else {
while (ptr < d->limit) { while (!decode_isdone(d, &ptr)) {
decode_parseret ret = decode_field(d, ptr, msg, layout); decode_parseret ret = decode_field(d, ptr, msg, layout);
ptr = ret.ptr; ptr = ret.ptr;
if (ret.group_end) return ptr; if (ret.group_end) return ptr;
} }
} }
if (ptr != d->limit) decode_err(d);
return ptr; return ptr;
} }
@ -645,11 +668,22 @@ bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l,
bool ok; bool ok;
upb_decstate state; 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.limit_ptr = state.end;
state.fastlimit = state.limit - 16;
state.fastend = state.fastlimit;
state.depth = 64; state.depth = 64;
state.end_group = 0; state.end_group = 0;
state.arena.head = arena->head; state.arena.head = arena->head;

@ -15,12 +15,14 @@
#include "upb/port_def.inc" #include "upb/port_def.inc"
typedef struct upb_decstate { typedef struct upb_decstate {
const char *limit; /* End of delimited region or end of buffer. */ const char *end; /* Can read up to 16 bytes slop beyond this. */
const char *fastend; /* The end of the entire buffer - 16 */ const char *limit_ptr; /* = end + UPB_MIN(limit, 0) */
const char *fastlimit; /* UPB_MIN(limit, fastend) */ int limit; /* Submessage limit relative to end. */
upb_arena arena;
int depth; int depth;
uint32_t end_group; /* Set to field number of END_GROUP tag, if any. */ 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; jmp_buf err;
} upb_decstate; } upb_decstate;
@ -36,6 +38,57 @@ const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
* noreturn. */ * noreturn. */
const char *fastdecode_err(upb_decstate *d); 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" #include "upb/port_undef.inc"
#endif /* UPB_DECODE_INT_H_ */ #endif /* UPB_DECODE_INT_H_ */

@ -72,19 +72,31 @@ static uint32_t fastdecode_loadtag(const char *ptr) {
return tag; 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 UPB_FORCEINLINE
const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg, const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
const upb_msglayout *table, uint64_t hasbits) { const upb_msglayout *table, uint64_t hasbits) {
if (UPB_UNLIKELY(ptr >= d->fastlimit)) { if (UPB_UNLIKELY(ptr >= d->limit_ptr)) {
if (UPB_LIKELY(ptr == d->limit)) { int overrun = ptr - d->end;
if (UPB_LIKELY(overrun == d->limit)) {
// Parse is finished. // Parse is finished.
*(uint32_t*)msg |= hasbits >> 16; // Sync hasbits. *(uint32_t*)msg |= hasbits >> 16; // Sync hasbits.
return ptr; 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). // 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, static bool fastdecode_boundscheck(const char *ptr, size_t len,
const char *end) { const char *end) {
uintptr_t uptr = (uintptr_t)ptr; uintptr_t uptr = (uintptr_t)ptr;
uintptr_t uend = (uintptr_t)end; uintptr_t uend = (uintptr_t)end + 16;
uintptr_t res = uptr + len; uintptr_t res = uptr + len;
return res < uptr || res > uend; 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 UPB_FORCEINLINE
static const char *fastdecode_string(UPB_PARSE_PARAMS, int tagbytes, static const char *fastdecode_string(UPB_PARSE_PARAMS, int tagbytes,
upb_card card) { upb_card card) {
@ -268,10 +295,13 @@ static const char *fastdecode_string(UPB_PARSE_PARAMS, int tagbytes,
str = ptr + tagbytes + 1; str = ptr + tagbytes + 1;
dst->data = str; dst->data = str;
dst->size = len; 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; dst->size = 0;
RETURN_GENERIC("string field len >1 byte\n"); 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); return fastdecode_dispatch(d, str + len, msg, table, hasbits);
} }
@ -293,37 +323,6 @@ const char *upb_pos_2bt(UPB_PARSE_PARAMS) {
/* message fields *************************************************************/ /* 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 UPB_FORCEINLINE
static const char *fastdecode_submsg(UPB_PARSE_PARAMS, int tagbytes, static const char *fastdecode_submsg(UPB_PARSE_PARAMS, int tagbytes,
int msg_ceil_bytes, upb_card card) { int msg_ceil_bytes, upb_card card) {
@ -348,9 +347,6 @@ static const char *fastdecode_submsg(UPB_PARSE_PARAMS, int tagbytes,
hasbits = 0; hasbits = 0;
} }
const char *saved_limit = d->limit;
const char *saved_fastlimit = d->fastlimit;
again: again:
if (card == CARD_r) { if (card == CARD_r) {
if (UPB_UNLIKELY(submsg == end)) { if (UPB_UNLIKELY(submsg == end)) {
@ -374,16 +370,37 @@ again:
} }
ptr += tagbytes + 1; ptr += tagbytes + 1;
size_t len = (uint8_t)ptr[-1];
ptr = fastdecode_tosubmsg(d, ptr, child, subl, 0, saved_limit); int saved_delta;
if (UPB_UNLIKELY(len & 0x80)) {
if (UPB_UNLIKELY(ptr != d->limit || d->end_group != 0)) { 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); return fastdecode_err(d);
} }
if (card == CARD_r) { if (card == CARD_r) {
submsg++; submsg++;
if (UPB_LIKELY(ptr < saved_fastlimit)) { if (UPB_LIKELY(!decode_isdone(d, &ptr))) {
uint32_t tag = fastdecode_loadtag(ptr); uint32_t tag = fastdecode_loadtag(ptr);
if (tagbytes == 1) { if (tagbytes == 1) {
if ((uint8_t)tag == (uint8_t)data) goto again; if ((uint8_t)tag == (uint8_t)data) goto again;
@ -391,34 +408,17 @@ again:
if ((uint16_t)tag == (uint16_t)data) goto again; if ((uint16_t)tag == (uint16_t)data) goto again;
} }
arr->len = submsg - (upb_msg**)_upb_array_ptr(arr); arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
d->limit = saved_limit;
d->fastlimit = saved_fastlimit;
d->depth++; d->depth++;
return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag); return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag);
} else { } else {
if (ptr == saved_limit) {
arr->len = submsg - (upb_msg**)_upb_array_ptr(arr); arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
d->limit = saved_limit;
d->fastlimit = saved_fastlimit;
d->depth++; d->depth++;
return ptr; return ptr;
} }
goto repeated_generic;
}
} }
d->limit = saved_limit;
d->fastlimit = saved_fastlimit;
d->depth++; d->depth++;
return fastdecode_dispatch(d, ptr, msg, table, hasbits); 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) \ #define F(card, tagbytes, size_ceil, ceil_arg) \

Loading…
Cancel
Save