diff --git a/upb/decode.c b/upb/decode.c index da6586cb65..3dc5516cfe 100644 --- a/upb/decode.c +++ b/upb/decode.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; diff --git a/upb/decode.int.h b/upb/decode.int.h index c49521fa21..b9134a436c 100644 --- a/upb/decode.int.h +++ b/upb/decode.int.h @@ -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_ */ diff --git a/upb/decode_fast.c b/upb/decode_fast.c index 32a7f60e49..c8961d77eb 100644 --- a/upb/decode_fast.c +++ b/upb/decode_fast.c @@ -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) \