Made a common longjmp()-handling layer, which can optionally be used on upb_EpsCopyInputStream.

`upb_EpsCopyInputStream` and `upb_WireReader` can now report all errors via `longjmp()` when an error handler is installed.

This lets us unwind some complexity:

* The decoder can now just use `upb_WireReader_Read*()`, instead of having a separate copy of those functions solely so that errors can `longjmp()`.
* `upb_EpsCopyInputStream` no longer needs to support a custom callback for `IsDoneFallback`, which existed solely so that errors could `longjmp()`.  This allows us to move `upb_EpsCopyInputStream_IsDoneFallback()` out of the header and into the source file, where fallback functions belong.

In future CLs we can apply the same pattern to `upb_Arena` so that allocation failures will `longjmp()`.

PiperOrigin-RevId: 843422118
This commit is contained in:
Joshua Haberman 2025-12-11 16:35:07 -08:00 committed by Copybara-Service
parent 831bb2cb7f
commit be01da55ac
20 changed files with 357 additions and 291 deletions

View file

@ -155,6 +155,7 @@ google/protobuf/wire_format_lite.h
google/protobuf/wrappers.pb.h
google/protobuf/wrappers.proto
upb/base/descriptor_constants.h
upb/base/error_handler.h
upb/base/status.h
upb/base/status.hpp
upb/base/string_view.h

View file

@ -31,8 +31,8 @@ pub enum EncodeStatus {
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum DecodeStatus {
Ok = 0,
Malformed = 1,
OutOfMemory = 2,
OutOfMemory = 1,
Malformed = 2,
BadUtf8 = 3,
MaxDepthExceeded = 4,
MissingRequired = 5,

View file

@ -18,6 +18,7 @@ cc_library(
],
hdrs = [
"descriptor_constants.h",
"error_handler.h",
"status.h",
"status.hpp",
"string_view.h",

79
upb/base/error_handler.h Normal file
View file

@ -0,0 +1,79 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef GOOGLE_UPB_UPB_BASE_ERROR_HANDLER_H__
#define GOOGLE_UPB_UPB_BASE_ERROR_HANDLER_H__
#include <setjmp.h>
// Must be last.
#include "upb/port/def.inc"
// upb_ErrorHandler is a standard longjmp()-based exception handler for UPB.
// It is used for efficient error handling in cases where longjmp() is safe to
// use, such as in highly performance-sensitive C parsing code.
//
// This structure contains both a jmp_buf and an error code; the error code is
// stored in the structure prior to calling longjmp(). This is necessary because
// per the C standard, it is not possible to store the result of setjmp(), so
// the error code must be passed out-of-band.
//
// upb_ErrorHandler is generally not C++-compatible, because longjmp() does not
// run C++ destructors. So any library that supports upb_ErrorHandler should
// also support a regular return-based error handling mechanism. (Note: we
// could conceivably extend this to take a callback, which could either call
// longjmp() or throw a C++ exception. But since C++ exceptions are forbidden
// by the C++ style guide, there's not likely to be a demand for this.)
//
// To support both cases (longjmp() or return-based status) efficiently, code
// can be written like this:
//
// UPB_ATTR_CONST bool upb_Arena_HasErrHandler(const upb_Arena* a);
//
// INLINE void* upb_Arena_Malloc(upb_Arena* a, size_t size) {
// if (UPB_UNLIKELY(a->end - a->ptr < size)) {
// void* ret = upb_Arena_MallocFallback(a, size);
// UPB_MAYBE_ASSUME(upb_Arena_HasErrHandler(a), ret != NULL);
// return ret;
// }
// void* ret = a->ptr;
// a->ptr += size;
// UPB_ASSUME(ret != NULL);
// return ret;
// }
//
// If the optimizer can prove that an error handler is present, it can assume
// that upb_Arena_Malloc() will not return NULL.
// We need to standardize on any error code that might be thrown by an error
// handler.
typedef enum {
kUpb_ErrorCode_Ok = 0,
kUpb_ErrorCode_OutOfMemory = 1,
kUpb_ErrorCode_Malformed = 2,
} upb_ErrorCode;
typedef struct {
int code;
jmp_buf buf;
} upb_ErrorHandler;
UPB_INLINE void upb_ErrorHandler_Init(upb_ErrorHandler* e) {
e->code = kUpb_ErrorCode_Ok;
}
UPB_INLINE UPB_NORETURN void upb_ErrorHandler_ThrowError(upb_ErrorHandler* e,
int code) {
UPB_ASSERT(code != kUpb_ErrorCode_Ok);
e->code = code;
UPB_LONGJMP(e->buf, 1);
}
#include "upb/port/undef.inc"
#endif // GOOGLE_UPB_UPB_BASE_ERROR_HANDLER_H__

View file

@ -317,6 +317,15 @@ Error, UINTPTR_MAX is undefined
#define UPB_ASSUME(expr) assert(expr)
#endif
#if UPB_HAS_BUILTIN(__builtin_constant_p) && UPB_HAS_ATTRIBUTE(const)
#define UPB_MAYBE_ASSUME(pred, x) \
if (__builtin_constant_p(pred) && pred) UPB_ASSUME(x)
#define UPB_ATTR_CONST __attribute__((const))
#else
#define UPB_MAYBE_ASSUME(pred, x)
#define UPB_ATTR_CONST
#endif
/* UPB_ASSERT(): in release mode, we use the expression without letting it be
* evaluated. This prevents "unused variable" warnings. */
#ifdef NDEBUG

View file

@ -88,3 +88,5 @@
#undef UPB_ARM64_ASM
#undef UPB_ARM64_BTI_DEFAULT
#undef UPB_DEPRECATE_AND_INLINE
#undef UPB_MAYBE_ASSUME
#undef UPB_ATTR_CONST

View file

@ -14,6 +14,7 @@
#include <string.h>
#include "upb/base/descriptor_constants.h"
#include "upb/base/error_handler.h"
#include "upb/base/internal/endian.h"
#include "upb/base/string_view.h"
#include "upb/hash/common.h"
@ -95,9 +96,15 @@ typedef union {
uint32_t size;
} wireval;
static void _upb_Decoder_AssumeEpsHasErrorHandler(upb_Decoder* d) {
UPB_ASSUME(upb_EpsCopyInputStream_HasErrorHandler(&d->input));
}
#define EPS(d) (_upb_Decoder_AssumeEpsHasErrorHandler(d), &(d)->input)
static void _upb_Decoder_VerifyUtf8(upb_Decoder* d, const char* buf, int len) {
if (!_upb_Decoder_VerifyUtf8Inline(buf, len)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_BadUtf8);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_BadUtf8);
}
}
@ -106,7 +113,7 @@ static bool _upb_Decoder_Reserve(upb_Decoder* d, upb_Array* arr, size_t elem) {
arr->UPB_PRIVATE(capacity) - arr->UPB_PRIVATE(size) < elem;
if (need_realloc && !UPB_PRIVATE(_upb_Array_Realloc)(
arr, arr->UPB_PRIVATE(size) + elem, &d->arena)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
return need_realloc;
}
@ -116,79 +123,15 @@ typedef struct {
uint64_t val;
} _upb_DecodeLongVarintReturn;
UPB_NOINLINE
static _upb_DecodeLongVarintReturn _upb_Decoder_DecodeLongVarint(
const char* ptr, uint64_t val, upb_Decoder* d) {
uint64_t byte;
for (int i = 1; i < 10; i++) {
byte = (uint8_t)ptr[i];
val += (byte - 1) << (i * 7);
if (!(byte & 0x80)) {
return (_upb_DecodeLongVarintReturn){.ptr = ptr + i + 1, .val = val};
}
}
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
}
UPB_NOINLINE
static _upb_DecodeLongVarintReturn _upb_Decoder_DecodeLongTag(const char* ptr,
uint64_t val,
upb_Decoder* d) {
uint64_t byte;
for (int i = 1; i < 5; i++) {
byte = (uint8_t)ptr[i];
val += (byte - 1) << (i * 7);
if (!(byte & 0x80)) {
if (val > UINT32_MAX) {
break;
}
return (_upb_DecodeLongVarintReturn){.ptr = ptr + i + 1, .val = val};
}
}
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
}
UPB_FORCEINLINE
const char* _upb_Decoder_DecodeVarint(upb_Decoder* d, const char* ptr,
uint64_t* val) {
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(&d->input, 10);
uint64_t byte = (uint8_t)*ptr;
if (UPB_LIKELY((byte & 0x80) == 0)) {
*val = byte;
return ptr + 1;
} else {
_upb_DecodeLongVarintReturn res =
_upb_Decoder_DecodeLongVarint(ptr, byte, d);
*val = res.val;
return res.ptr;
}
}
UPB_FORCEINLINE
const char* _upb_Decoder_DecodeTag(upb_Decoder* d, const char* ptr,
uint32_t* val) {
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(&d->input, 5);
uint64_t byte = (uint8_t)*ptr;
if (UPB_LIKELY((byte & 0x80) == 0)) {
*val = byte;
return ptr + 1;
} else {
_upb_DecodeLongVarintReturn res = _upb_Decoder_DecodeLongTag(ptr, byte, d);
*val = res.val;
return res.ptr;
}
}
UPB_FORCEINLINE
const char* upb_Decoder_DecodeSize(upb_Decoder* d, const char* ptr,
uint32_t* size) {
uint64_t size64;
ptr = _upb_Decoder_DecodeVarint(d, ptr, &size64);
if (size64 >= INT32_MAX ||
!upb_EpsCopyInputStream_CheckSize(&d->input, ptr, (int)size64)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
int sz;
ptr = upb_WireReader_ReadSize(ptr, &sz, EPS(d));
if (!upb_EpsCopyInputStream_CheckSize(&d->input, ptr, sz)) {
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
*size = size64;
*size = sz;
return ptr;
}
@ -229,7 +172,7 @@ static upb_Message* _upb_Decoder_NewSubMessage2(upb_Decoder* d,
upb_Message** target) {
UPB_ASSERT(subl);
upb_Message* msg = _upb_Message_New(subl, &d->arena);
if (!msg) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
if (!msg) upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
*target = msg;
return msg;
@ -245,7 +188,7 @@ static upb_Message* _upb_Decoder_NewSubMessage(upb_Decoder* d,
static const char* _upb_Decoder_ReadString2(upb_Decoder* d, const char* ptr,
int size, upb_StringView* str) {
if (!_upb_Decoder_ReadString(d, &ptr, size, str)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
return ptr;
}
@ -256,12 +199,12 @@ const char* _upb_Decoder_RecurseSubMessage(upb_Decoder* d, const char* ptr,
const upb_MiniTable* subl,
uint32_t expected_end_group) {
if (--d->depth < 0) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_MaxDepthExceeded);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_MaxDepthExceeded);
}
ptr = _upb_Decoder_DecodeMessage(d, ptr, submsg, subl);
d->depth++;
if (d->end_group != expected_end_group) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
return ptr;
}
@ -272,9 +215,6 @@ const char* _upb_Decoder_DecodeSubMessage(upb_Decoder* d, const char* ptr,
const upb_MiniTableField* field,
size_t size) {
ptrdiff_t delta = upb_EpsCopyInputStream_PushLimit(&d->input, ptr, size);
if (UPB_UNLIKELY(delta < 0)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
}
const upb_MiniTable* subl = upb_MiniTable_GetSubMessageTable(field);
UPB_ASSERT(subl);
ptr = _upb_Decoder_RecurseSubMessage(d, ptr, submsg, subl, DECODE_NOGROUP);
@ -287,8 +227,8 @@ const char* _upb_Decoder_DecodeGroup(upb_Decoder* d, const char* ptr,
upb_Message* submsg,
const upb_MiniTable* subl,
uint32_t number) {
if (_upb_Decoder_IsDone(d, &ptr)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
if (upb_EpsCopyInputStream_IsDone(EPS(d), &ptr)) {
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
ptr = _upb_Decoder_RecurseSubMessage(d, ptr, submsg, subl, number);
d->end_group = DECODE_NOGROUP;
@ -335,7 +275,7 @@ void _upb_Decoder_AddEnumValueToUnknown(upb_Decoder* d, upb_Message* msg,
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(unknown_msg, buf, end - buf,
&d->arena, kUpb_AddUnknown_Copy)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
}
@ -347,9 +287,10 @@ const char* _upb_Decoder_DecodeFixedPacked(upb_Decoder* d, const char* ptr,
upb_StringView sv;
ptr = upb_EpsCopyInputStream_ReadStringEphemeral(&d->input, ptr, val->size,
&sv);
if (!ptr) upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
int mask = (1 << lg2) - 1;
if (UPB_UNLIKELY((val->size & mask) != 0 || ptr == NULL)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
size_t count = val->size >> lg2;
if (count == 0) return ptr;
@ -391,14 +332,11 @@ const char* _upb_Decoder_DecodeVarintPacked(upb_Decoder* d, const char* ptr,
int lg2) {
int scale = 1 << lg2;
ptrdiff_t delta = upb_EpsCopyInputStream_PushLimit(&d->input, ptr, val->size);
if (UPB_UNLIKELY(delta < 0)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
}
char* out = UPB_PTR_AT(upb_Array_MutableDataPtr(arr),
arr->UPB_PRIVATE(size) << lg2, void);
while (!_upb_Decoder_IsDone(d, &ptr)) {
while (!upb_EpsCopyInputStream_IsDone(EPS(d), &ptr)) {
wireval elem;
ptr = _upb_Decoder_DecodeVarint(d, ptr, &elem.uint64_val);
ptr = upb_WireReader_ReadVarint(ptr, &elem.uint64_val, EPS(d));
_upb_Decoder_Munge(field, &elem);
if (_upb_Decoder_Reserve(d, arr, 1)) {
out = UPB_PTR_AT(upb_Array_MutableDataPtr(arr),
@ -418,14 +356,11 @@ static const char* _upb_Decoder_DecodeEnumPacked(
const upb_MiniTableField* field, wireval* val) {
const upb_MiniTableEnum* e = upb_MiniTable_GetSubEnumTable(field);
ptrdiff_t delta = upb_EpsCopyInputStream_PushLimit(&d->input, ptr, val->size);
if (UPB_UNLIKELY(delta < 0)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
}
char* out = UPB_PTR_AT(upb_Array_MutableDataPtr(arr),
arr->UPB_PRIVATE(size) * 4, void);
while (!_upb_Decoder_IsDone(d, &ptr)) {
while (!upb_EpsCopyInputStream_IsDone(EPS(d), &ptr)) {
wireval elem;
ptr = _upb_Decoder_DecodeVarint(d, ptr, &elem.uint64_val);
ptr = upb_WireReader_ReadVarint(ptr, &elem.uint64_val, EPS(d));
if (!upb_MiniTableEnum_CheckValue(e, elem.uint64_val)) {
_upb_Decoder_AddEnumValueToUnknown(d, msg, field, &elem);
continue;
@ -447,7 +382,7 @@ static upb_Array* _upb_Decoder_CreateArray(upb_Decoder* d,
const upb_FieldType field_type = field->UPB_PRIVATE(descriptortype);
const size_t lg2 = UPB_PRIVATE(_upb_FieldType_SizeLg2)(field_type);
upb_Array* ret = UPB_PRIVATE(_upb_Array_New)(&d->arena, 4, lg2);
if (!ret) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
if (!ret) upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
return ret;
}
@ -548,7 +483,7 @@ static upb_Map* _upb_Decoder_CreateMap(upb_Decoder* d,
UPB_ASSERT(key_field->UPB_PRIVATE(offset) == offsetof(upb_MapEntry, k));
UPB_ASSERT(val_field->UPB_PRIVATE(offset) == offsetof(upb_MapEntry, v));
upb_Map* ret = _upb_Map_New(&d->arena, key_size, val_size);
if (!ret) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
if (!ret) upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
return ret;
}
@ -560,7 +495,7 @@ UPB_NOINLINE static void _upb_Decoder_AddMapEntryUnknown(
upb_EncodeStatus status =
upb_Encode(ent_msg, entry, 0, &d->arena, &buf, &size);
if (status != kUpb_EncodeStatus_Ok) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
char delim_buf[2 * kUpb_Decoder_EncodeVarint32MaxSize];
char* delim_end = delim_buf;
@ -574,7 +509,7 @@ UPB_NOINLINE static void _upb_Decoder_AddMapEntryUnknown(
};
if (!UPB_PRIVATE(_upb_Message_AddUnknownV)(msg, &d->arena, unknown, 2)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
}
@ -617,7 +552,7 @@ static const char* _upb_Decoder_DecodeToMap(upb_Decoder* d, const char* ptr,
} else {
if (_upb_Map_Insert(map, &ent.k, map->key_size, &ent.v, map->val_size,
&d->arena) == kUpb_MapInsertStatus_OutOfMemory) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
}
return ptr;
@ -676,13 +611,6 @@ static const char* _upb_Decoder_DecodeToSubMessage(
return ptr;
}
static const char* upb_Decoder_SkipField(upb_Decoder* d, const char* ptr,
uint32_t tag) {
ptr = _upb_WireReader_SkipValue(ptr, tag, d->depth, &d->input);
if (!ptr) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
return ptr;
}
enum {
kStartItemTag = ((kUpb_MsgSet_Item << 3) | kUpb_WireType_StartGroup),
kEndItemTag = ((kUpb_MsgSet_Item << 3) | kUpb_WireType_EndGroup),
@ -696,7 +624,7 @@ static void upb_Decoder_AddKnownMessageSetItem(
upb_Extension* ext =
UPB_PRIVATE(_upb_Message_GetOrCreateExtension)(msg, item_mt, &d->arena);
if (UPB_UNLIKELY(!ext)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
upb_Message** submsgp = (upb_Message**)&ext->data.msg_val;
upb_Message* submsg = _upb_Decoder_NewSubMessage2(
@ -705,7 +633,9 @@ static void upb_Decoder_AddKnownMessageSetItem(
upb_DecodeStatus status = upb_Decode(
data, size, submsg, upb_MiniTableExtension_GetSubMessage(item_mt),
d->extreg, d->options, &d->arena);
if (status != kUpb_DecodeStatus_Ok) _upb_Decoder_ErrorJmp(d, status);
if (status != kUpb_DecodeStatus_Ok) {
upb_ErrorHandler_ThrowError(&d->err, status);
}
}
static void upb_Decoder_AddUnknownMessageSetItem(upb_Decoder* d,
@ -730,7 +660,7 @@ static void upb_Decoder_AddUnknownMessageSetItem(upb_Decoder* d,
{split, end - split},
};
if (!UPB_PRIVATE(_upb_Message_AddUnknownV)(msg, &d->arena, unknown, 3)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
}
@ -757,15 +687,15 @@ static const char* upb_Decoder_DecodeMessageSetItem(
kUpb_HavePayload = 1 << 1,
} StateMask;
StateMask state_mask = 0;
while (!_upb_Decoder_IsDone(d, &ptr)) {
while (!upb_EpsCopyInputStream_IsDone(EPS(d), &ptr)) {
uint32_t tag;
ptr = _upb_Decoder_DecodeTag(d, ptr, &tag);
ptr = upb_WireReader_ReadTag(ptr, &tag, EPS(d));
switch (tag) {
case kEndItemTag:
return ptr;
case kTypeIdTag: {
uint64_t tmp;
ptr = _upb_Decoder_DecodeVarint(d, ptr, &tmp);
ptr = upb_WireReader_ReadVarint(ptr, &tmp, EPS(d));
if (state_mask & kUpb_HaveId) break; // Ignore dup.
state_mask |= kUpb_HaveId;
type_id = tmp;
@ -781,7 +711,9 @@ static const char* upb_Decoder_DecodeMessageSetItem(
ptr = upb_Decoder_DecodeSize(d, ptr, &size);
ptr = upb_EpsCopyInputStream_ReadStringAlwaysAlias(&d->input, ptr, size,
&sv);
if (!ptr) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
if (!ptr) {
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
if (state_mask & kUpb_HavePayload) break; // Ignore dup.
state_mask |= kUpb_HavePayload;
if (state_mask & kUpb_HaveId) {
@ -795,11 +727,11 @@ static const char* upb_Decoder_DecodeMessageSetItem(
}
default:
// We do not preserve unexpected fields inside a message set item.
ptr = upb_Decoder_SkipField(d, ptr, tag);
ptr = _upb_WireReader_SkipValue(ptr, tag, d->depth, &d->input);
break;
}
}
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
static upb_MiniTableField upb_Decoder_FieldNotFoundField = {
@ -981,7 +913,7 @@ const char* _upb_Decoder_DecodeWireValue(upb_Decoder* d, const char* ptr,
switch (wire_type) {
case kUpb_WireType_Varint:
ptr = _upb_Decoder_DecodeVarint(d, ptr, &val->uint64_val);
ptr = upb_WireReader_ReadVarint(ptr, &val->uint64_val, EPS(d));
if (upb_MiniTableField_IsClosedEnum(field)) {
const upb_MiniTableEnum* e = upb_MiniTable_GetSubEnumTable(field);
if (!upb_MiniTableEnum_CheckValue(e, val->uint64_val)) {
@ -1024,7 +956,7 @@ const char* _upb_Decoder_DecodeWireValue(upb_Decoder* d, const char* ptr,
default:
break;
}
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
UPB_FORCEINLINE
@ -1041,7 +973,7 @@ const char* _upb_Decoder_DecodeKnownField(upb_Decoder* d, const char* ptr,
upb_Extension* ext = UPB_PRIVATE(_upb_Message_GetOrCreateExtension)(
msg, ext_layout, &d->arena);
if (UPB_UNLIKELY(!ext)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
d->original_msg = msg;
msg = &ext->data.UPB_PRIVATE(ext_msg_val);
@ -1113,7 +1045,9 @@ static const char* _upb_Decoder_FindFieldStart(upb_Decoder* d, const char* ptr,
static const char* _upb_Decoder_DecodeUnknownField(
upb_Decoder* d, const char* ptr, upb_Message* msg, uint32_t field_number,
uint32_t wire_type, wireval val) {
if (field_number == 0) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
if (field_number == 0) {
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
}
const char* start =
_upb_Decoder_FindFieldStart(d, ptr, field_number, wire_type);
@ -1124,18 +1058,14 @@ static const char* _upb_Decoder_DecodeUnknownField(
upb_StringView sv;
ptr = upb_EpsCopyInputStream_ReadStringEphemeral(&d->input, ptr, val.size,
&sv);
if (UPB_UNLIKELY(ptr == NULL)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
}
if (!ptr) upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_Malformed);
} else if (wire_type == kUpb_WireType_StartGroup) {
ptr = UPB_PRIVATE(_upb_WireReader_SkipGroup)(ptr, field_number << 3,
d->depth, &d->input);
}
upb_StringView sv;
if (ptr == NULL || !upb_EpsCopyInputStream_EndCapture(&d->input, ptr, &sv)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
}
upb_EpsCopyInputStream_EndCapture(&d->input, ptr, &sv);
upb_AddUnknownMode mode = kUpb_AddUnknown_Copy;
if (d->options & kUpb_DecodeOption_AliasString) {
@ -1150,7 +1080,7 @@ static const char* _upb_Decoder_DecodeUnknownField(
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(msg, sv.data, sv.size, &d->arena,
mode)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
upb_ErrorHandler_ThrowError(&d->err, kUpb_DecodeStatus_OutOfMemory);
}
return ptr;
@ -1166,7 +1096,7 @@ const char* _upb_Decoder_DecodeFieldTag(upb_Decoder* d, const char* ptr,
uint32_t tag;
UPB_ASSERT(ptr < d->input.limit_ptr);
ptr = _upb_Decoder_DecodeTag(d, ptr, &tag);
ptr = upb_WireReader_ReadTag(ptr, &tag, EPS(d));
*field_number = tag >> 3;
*wire_type = tag & 7;
return ptr;
@ -1273,7 +1203,7 @@ const char* _upb_Decoder_DecodeField(upb_Decoder* d, const char* ptr,
if (_upb_Decoder_TryDecodeMessageFast(d, &ptr, msg, mt, last_field_index,
data)) {
return ptr;
} else if (_upb_Decoder_IsDone(d, &ptr)) {
} else if (upb_EpsCopyInputStream_IsDone(EPS(d), &ptr)) {
return _upb_Decoder_EndMessage(d, ptr);
}
@ -1312,10 +1242,10 @@ static upb_DecodeStatus upb_Decoder_Decode(upb_Decoder* const decoder,
upb_Message* const msg,
const upb_MiniTable* const m,
upb_Arena* const arena) {
if (UPB_SETJMP(decoder->err) == 0) {
decoder->status = _upb_Decoder_DecodeTop(decoder, buf, msg, m);
if (UPB_SETJMP(decoder->err.buf) == 0) {
decoder->err.code = _upb_Decoder_DecodeTop(decoder, buf, msg, m);
} else {
UPB_ASSERT(decoder->status != kUpb_DecodeStatus_Ok);
UPB_ASSERT(decoder->err.code != kUpb_DecodeStatus_Ok);
}
return upb_Decoder_Destroy(decoder, arena);

View file

@ -83,8 +83,8 @@ UPB_INLINE int upb_Decode_LimitDepth(uint32_t decode_options, uint32_t limit) {
// LINT.IfChange
typedef enum {
kUpb_DecodeStatus_Ok = 0,
kUpb_DecodeStatus_Malformed = 1, // Wire format was corrupt
kUpb_DecodeStatus_OutOfMemory = 2, // Arena alloc failed
kUpb_DecodeStatus_OutOfMemory = 1, // Arena alloc failed
kUpb_DecodeStatus_Malformed = 2, // Wire format was corrupt
kUpb_DecodeStatus_BadUtf8 = 3, // String field had bad UTF-8
kUpb_DecodeStatus_MaxDepthExceeded =
4, // Exceeded upb_DecodeOptions_MaxDepth

View file

@ -10,6 +10,6 @@ const char* upb_DecodeFast_IsDoneFallback(upb_Decoder* d, const char* ptr) {
upb_IsDoneStatus status = UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneStatus)(
&d->input, ptr, &overrun);
UPB_ASSERT(status == kUpb_IsDoneStatus_NeedFallback);
return UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackInline)(
&d->input, ptr, overrun, _upb_Decoder_BufferFlipCallback);
return UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallback)(&d->input, ptr,
overrun);
}

View file

@ -94,7 +94,7 @@ fastdecode_nextret fastdecode_nextrepeated(upb_Decoder* d, void* dst,
fastdecode_nextret ret;
dst = (char*)dst + valbytes;
if (UPB_LIKELY(!_upb_Decoder_IsDone(d, ptr))) {
if (UPB_LIKELY(!upb_EpsCopyInputStream_IsDone(&d->input, ptr))) {
ret.tag = _upb_FastDecoder_LoadTag(*ptr);
if (fastdecode_tagmatch(ret.tag, data, tagbytes)) {
ret.next = FD_NEXT_SAMEFIELD;

View file

@ -34,8 +34,8 @@ UPB_NOINLINE UPB_PRESERVE_NONE const char* upb_DecodeFast_MessageIsDoneFallback(
}
case kUpb_IsDoneStatus_NeedFallback:
// We've reached end-of-buffer. Refresh the buffer.
ptr = UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackInline)(
&d->input, ptr, overrun, _upb_Decoder_BufferFlipCallback);
ptr = UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallback)(&d->input, ptr,
overrun);
// We successfully refreshed the buffer (otherwise the function above
// would have thrown an error with longjmp()). So continue with the
@ -49,7 +49,7 @@ UPB_NOINLINE UPB_PRESERVE_NONE const char* upb_DecodeFast_MessageIsDoneFallback(
}
const char* _upb_FastDecoder_ErrorJmp2(upb_Decoder* d) {
UPB_LONGJMP(d->err, 1);
UPB_LONGJMP(d->err.buf, 1);
return NULL;
}

View file

@ -202,7 +202,7 @@ const char* _upb_FastDecoder_ErrorJmp2(upb_Decoder* d);
UPB_INLINE
const char* _upb_FastDecoder_ErrorJmp(upb_Decoder* d, upb_DecodeStatus status) {
d->status = status;
d->err.code = status;
return _upb_FastDecoder_ErrorJmp2(d);
}
@ -213,7 +213,7 @@ const char* _upb_FastDecoder_ErrorJmp(upb_Decoder* d, upb_DecodeStatus status) {
case kUpb_DecodeFastNext_FallbackToMiniTable: \
UPB_MUSTTAIL return _upb_FastDecoder_DecodeGeneric(UPB_PARSE_ARGS); \
case kUpb_DecodeFastNext_Error: \
UPB_ASSERT(d->status != kUpb_DecodeStatus_Ok); \
UPB_ASSERT(d->err.code != kUpb_DecodeStatus_Ok); \
return _upb_FastDecoder_ErrorJmp2(d); \
case kUpb_DecodeFastNext_MessageIsDoneFallback: \
UPB_MUSTTAIL return upb_DecodeFast_MessageIsDoneFallback( \
@ -253,7 +253,7 @@ UPB_INLINE bool upb_DecodeFast_SetError(upb_Decoder* d,
#ifdef UPB_TRACE_FASTDECODER
fprintf(stderr, "Fasttable error @ %s:%d -> %s (%d)\n", file, line, sym, val);
#endif
d->status = val;
d->err.code = val;
*next = kUpb_DecodeFastNext_Error;
return false;
}

View file

@ -7,16 +7,42 @@
#include "upb/wire/eps_copy_input_stream.h"
#include <stdint.h>
#include <string.h>
#include "upb/base/error_handler.h"
#include "upb/wire/internal/eps_copy_input_stream.h"
// Must be last.
#include "upb/port/def.inc"
static const char* _upb_EpsCopyInputStream_NoOpCallback(
upb_EpsCopyInputStream* e, const char* old_end, const char* new_start) {
return new_start;
const char* UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(
upb_EpsCopyInputStream* e) {
e->error = true;
if (e->err) upb_ErrorHandler_ThrowError(e->err, kUpb_ErrorCode_Malformed);
return NULL;
}
const char* UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackNoCallback)(
upb_EpsCopyInputStream* e, const char* ptr, int overrun) {
return UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackInline)(
e, ptr, overrun, _upb_EpsCopyInputStream_NoOpCallback);
const char* UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallback)(
struct upb_EpsCopyInputStream* e, const char* ptr, int overrun) {
if (overrun < e->limit) {
// Need to copy remaining data into patch buffer.
UPB_ASSERT(overrun < kUpb_EpsCopyInputStream_SlopBytes);
const char* old_end = ptr;
const char* new_start = &e->patch[overrun];
memset(&e->patch[kUpb_EpsCopyInputStream_SlopBytes], 0,
kUpb_EpsCopyInputStream_SlopBytes);
memcpy(e->patch, e->end, kUpb_EpsCopyInputStream_SlopBytes);
ptr = new_start;
e->end = &e->patch[kUpb_EpsCopyInputStream_SlopBytes];
e->limit -= kUpb_EpsCopyInputStream_SlopBytes;
e->limit_ptr = e->end + e->limit;
UPB_ASSERT(ptr < e->limit_ptr);
e->input_delta = (uintptr_t)old_end - (uintptr_t)new_start;
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsChecked)(e);
return new_start;
} else {
UPB_ASSERT(overrun > e->limit);
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(e);
}
}

View file

@ -11,6 +11,7 @@
#include <stddef.h>
#include <stdint.h>
#include "upb/base/error_handler.h"
#include "upb/base/string_view.h"
#include "upb/wire/internal/eps_copy_input_stream.h"
@ -29,6 +30,24 @@ typedef struct upb_EpsCopyInputStream upb_EpsCopyInputStream;
UPB_INLINE void upb_EpsCopyInputStream_Init(upb_EpsCopyInputStream* e,
const char** ptr, size_t size);
// Like the previous function, but registers an error handler that will be
// called for any errors encountered.
UPB_INLINE void upb_EpsCopyInputStream_InitWithErrorHandler(
upb_EpsCopyInputStream* e, const char** ptr, size_t size,
upb_ErrorHandler* err);
// Returns true if the stream has an error handler.
//
// This function is marked const, which indicates to the compiler that the
// return value is solely a function of the pointer value. This is not
// entirely true if the stream is reinitialized with
// upb_EpsCopyInputStream_Init*(), so users must not call this function in
// any context where the stream may be reinitialized between calls to this
// function, and the presence of an error handler changes when reinitialized.
UPB_ATTR_CONST
UPB_INLINE bool upb_EpsCopyInputStream_HasErrorHandler(
const upb_EpsCopyInputStream* e);
// Returns true if the stream is in the error state. A stream enters the error
// state when the user reads past a limit (caught in IsDone()) or the
// ZeroCopyInputStream returns an error.

View file

@ -28,17 +28,3 @@ const char* _upb_Decoder_CheckRequired(upb_Decoder* d, const char* ptr,
}
return ptr;
}
UPB_NORETURN void* _upb_Decoder_ErrorJmp(upb_Decoder* d,
upb_DecodeStatus status) {
UPB_ASSERT(status != kUpb_DecodeStatus_Ok);
d->status = status;
UPB_LONGJMP(d->err, 1);
}
UPB_NOINLINE
const char* _upb_Decoder_IsDoneFallback(upb_EpsCopyInputStream* e,
const char* ptr, int overrun) {
return UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackInline)(
e, ptr, overrun, _upb_Decoder_BufferFlipCallback);
}

View file

@ -19,6 +19,7 @@
#include <string.h>
#include "upb/base/descriptor_constants.h"
#include "upb/base/error_handler.h"
#include "upb/base/string_view.h"
#include "upb/mem/arena.h"
#include "upb/mem/internal/arena.h"
@ -50,8 +51,7 @@ typedef struct upb_Decoder {
upb_Arena arena;
void* foo[UPB_ARENA_SIZE_HACK];
};
upb_DecodeStatus status;
jmp_buf err;
upb_ErrorHandler err;
#ifndef NDEBUG
const char* debug_tagstart;
@ -67,7 +67,17 @@ UPB_INLINE const char* upb_Decoder_Init(upb_Decoder* d, const char* buf,
const upb_ExtensionRegistry* extreg,
int options, upb_Arena* arena,
char* trace_buf, size_t trace_size) {
upb_EpsCopyInputStream_Init(&d->input, &buf, size);
upb_ErrorHandler_Init(&d->err);
upb_EpsCopyInputStream_InitWithErrorHandler(&d->input, &buf, size, &d->err);
UPB_STATIC_ASSERT((int)kUpb_DecodeStatus_Ok == (int)kUpb_ErrorCode_Ok,
"mismatched error codes");
UPB_STATIC_ASSERT(
(int)kUpb_DecodeStatus_OutOfMemory == (int)kUpb_ErrorCode_OutOfMemory,
"mismatched error codes");
UPB_STATIC_ASSERT(
(int)kUpb_DecodeStatus_Malformed == (int)kUpb_ErrorCode_Malformed,
"mismatched error codes");
if (options & kUpb_DecodeOption_AlwaysValidateUtf8) {
// Fasttable decoder does not support this option.
@ -79,7 +89,6 @@ UPB_INLINE const char* upb_Decoder_Init(upb_Decoder* d, const char* buf,
d->end_group = DECODE_NOGROUP;
d->options = (uint16_t)options;
d->missing_required = false;
d->status = kUpb_DecodeStatus_Ok;
d->message_is_done = false;
#ifndef NDEBUG
d->trace_buf = trace_buf;
@ -100,7 +109,7 @@ UPB_INLINE const char* upb_Decoder_Init(upb_Decoder* d, const char* buf,
UPB_INLINE upb_DecodeStatus upb_Decoder_Destroy(upb_Decoder* d,
upb_Arena* arena) {
UPB_PRIVATE(_upb_Arena_SwapOut)(arena, &d->arena);
return d->status;
return (upb_DecodeStatus)d->err.code;
}
#ifndef NDEBUG
@ -168,28 +177,10 @@ UPB_INLINE const upb_MiniTable* decode_totablep(intptr_t table) {
return (const upb_MiniTable*)(table >> 8);
}
const char* _upb_Decoder_IsDoneFallback(upb_EpsCopyInputStream* e,
const char* ptr, int overrun);
const char* _upb_Decoder_DecodeMessage(upb_Decoder* d, const char* ptr,
upb_Message* msg,
const upb_MiniTable* layout);
UPB_INLINE bool _upb_Decoder_IsDone(upb_Decoder* d, const char** ptr) {
return UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneWithCallback)(
&d->input, ptr, &_upb_Decoder_IsDoneFallback);
}
UPB_NORETURN void* _upb_Decoder_ErrorJmp(upb_Decoder* d,
upb_DecodeStatus status);
UPB_INLINE const char* _upb_Decoder_BufferFlipCallback(
upb_EpsCopyInputStream* e, const char* old_end, const char* new_start) {
upb_Decoder* d = (upb_Decoder*)e;
if (!old_end) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);
return new_start;
}
UPB_INLINE bool _upb_Decoder_FieldRequiresUtf8Validation(
const upb_Decoder* d, const upb_MiniTableField* field) {
if (field->UPB_PRIVATE(descriptortype) == kUpb_FieldType_String) return true;

View file

@ -12,6 +12,7 @@
#include <stdint.h>
#include <string.h>
#include "upb/base/error_handler.h"
#include "upb/base/string_view.h"
// Must be last.
@ -38,6 +39,7 @@ struct upb_EpsCopyInputStream {
const char* buffer_start; // Pointer to the original input buffer
const char* capture_start; // If non-NULL, the start of the captured region.
ptrdiff_t limit; // Submessage limit relative to end
upb_ErrorHandler* err; // Error handler to use when things go wrong.
bool error; // To distinguish between EOF and error.
#ifndef NDEBUG
int guaranteed_bytes;
@ -56,10 +58,12 @@ UPB_INLINE bool upb_EpsCopyInputStream_IsError(
return e->error;
}
UPB_INLINE void upb_EpsCopyInputStream_Init(struct upb_EpsCopyInputStream* e,
const char** ptr, size_t size) {
UPB_INLINE void upb_EpsCopyInputStream_InitWithErrorHandler(
struct upb_EpsCopyInputStream* e, const char** ptr, size_t size,
upb_ErrorHandler* err) {
e->buffer_start = *ptr;
e->capture_start = NULL;
e->err = err;
if (size <= kUpb_EpsCopyInputStream_SlopBytes) {
memset(&e->patch, 0, 32);
if (size) memcpy(&e->patch, *ptr, size);
@ -77,12 +81,28 @@ UPB_INLINE void upb_EpsCopyInputStream_Init(struct upb_EpsCopyInputStream* e,
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsChecked)(e);
}
typedef const char* upb_EpsCopyInputStream_BufferFlipCallback(
struct upb_EpsCopyInputStream* e, const char* old_end,
const char* new_start);
UPB_INLINE void upb_EpsCopyInputStream_Init(struct upb_EpsCopyInputStream* e,
const char** ptr, size_t size) {
upb_EpsCopyInputStream_InitWithErrorHandler(e, ptr, size, NULL);
}
typedef const char* upb_EpsCopyInputStream_IsDoneFallbackFunc(
struct upb_EpsCopyInputStream* e, const char* ptr, int overrun);
UPB_ATTR_CONST
UPB_INLINE bool upb_EpsCopyInputStream_HasErrorHandler(
const struct upb_EpsCopyInputStream* e) {
return e && e->err != NULL;
}
// Call this function to signal an error. If an error handler is set, it will be
// called and the function will never return. Otherwise, returns NULL to
// indicate an error.
const char* UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(
struct upb_EpsCopyInputStream* e);
UPB_INLINE const char* UPB_PRIVATE(upb_EpsCopyInputStream_AssumeResult)(
struct upb_EpsCopyInputStream* e, const char* ptr) {
UPB_MAYBE_ASSUME(upb_EpsCopyInputStream_HasErrorHandler(e), ptr != NULL);
return ptr;
}
////////////////////////////////////////////////////////////////////////////////
@ -150,9 +170,11 @@ UPB_INLINE upb_IsDoneStatus UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneStatus)(
}
}
UPB_INLINE bool UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneWithCallback)(
struct upb_EpsCopyInputStream* e, const char** ptr,
upb_EpsCopyInputStream_IsDoneFallbackFunc* func) {
const char* UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallback)(
struct upb_EpsCopyInputStream* e, const char* ptr, int overrun);
UPB_INLINE bool upb_EpsCopyInputStream_IsDone(struct upb_EpsCopyInputStream* e,
const char** ptr) {
int overrun;
switch (UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneStatus)(e, *ptr, &overrun)) {
case kUpb_IsDoneStatus_Done:
@ -162,7 +184,8 @@ UPB_INLINE bool UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneWithCallback)(
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsChecked)(e);
return false;
case kUpb_IsDoneStatus_NeedFallback:
*ptr = func(e, *ptr, overrun);
*ptr =
UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallback)(e, *ptr, overrun);
if (*ptr) {
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsChecked)(e);
} else {
@ -173,15 +196,6 @@ UPB_INLINE bool UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneWithCallback)(
UPB_UNREACHABLE();
}
const char* UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackNoCallback)(
struct upb_EpsCopyInputStream* e, const char* ptr, int overrun);
UPB_INLINE bool upb_EpsCopyInputStream_IsDone(struct upb_EpsCopyInputStream* e,
const char** ptr) {
return UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneWithCallback)(
e, ptr, UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackNoCallback));
}
UPB_INLINE bool upb_EpsCopyInputStream_CheckSize(
const struct upb_EpsCopyInputStream* e, const char* ptr, int size) {
UPB_ASSERT(size >= 0);
@ -211,7 +225,9 @@ UPB_INLINE void upb_EpsCopyInputStream_StartCapture(
UPB_INLINE bool upb_EpsCopyInputStream_EndCapture(
struct upb_EpsCopyInputStream* e, const char* ptr, upb_StringView* sv) {
UPB_ASSERT(e->capture_start != NULL);
if (ptr - e->end > e->limit) return false;
if (ptr - e->end > e->limit) {
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(e);
}
const char* end = UPB_PRIVATE(upb_EpsCopyInputStream_GetInputPtr)(e, ptr);
sv->data = e->capture_start;
sv->size = end - sv->data;
@ -230,7 +246,12 @@ UPB_INLINE const char* upb_EpsCopyInputStream_ReadStringAlwaysAlias(
// buffer, so we must fail if the size extends into the slop bytes.
const char* limit =
e->end + (e->input_delta == 0) * kUpb_EpsCopyInputStream_SlopBytes;
if ((ptrdiff_t)size > limit - ptr) return NULL;
if ((ptrdiff_t)size > limit - ptr) {
// For the moment, we consider this an error. In a multi-buffer world,
// it could be that the requested string extends into the next buffer, which
// is not an error and should be recoverable.
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(e);
}
const char* input = UPB_PRIVATE(upb_EpsCopyInputStream_GetInputPtr)(e, ptr);
*sv = upb_StringView_FromDataAndSize(input, size);
return ptr + size;
@ -242,7 +263,12 @@ UPB_INLINE const char* upb_EpsCopyInputStream_ReadStringEphemeral(
UPB_ASSERT(size <= PTRDIFF_MAX);
// Size must be within the current buffer (including slop bytes).
const char* limit = e->end + kUpb_EpsCopyInputStream_SlopBytes;
if ((ptrdiff_t)size > limit - ptr) return NULL;
if ((ptrdiff_t)size > limit - ptr) {
// For the moment, we consider this an error. In a multi-buffer world,
// it could be that the requested string extends into the next buffer, which
// is not an error and should be recoverable.
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(e);
}
*sv = upb_StringView_FromDataAndSize(ptr, size);
return ptr + size;
}
@ -261,7 +287,9 @@ UPB_INLINE ptrdiff_t upb_EpsCopyInputStream_PushLimit(
e->limit = limit;
e->limit_ptr = e->end + UPB_MIN(0, limit);
UPB_PRIVATE(upb_EpsCopyInputStream_CheckLimit)(e);
if (UPB_UNLIKELY(delta < 0)) e->error = true;
if (UPB_UNLIKELY(delta < 0)) {
UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(e);
}
return delta;
}
@ -277,35 +305,6 @@ UPB_INLINE void upb_EpsCopyInputStream_PopLimit(
UPB_PRIVATE(upb_EpsCopyInputStream_CheckLimit)(e);
}
UPB_INLINE const char* UPB_PRIVATE(upb_EpsCopyInputStream_IsDoneFallbackInline)(
struct upb_EpsCopyInputStream* e, const char* ptr, int overrun,
upb_EpsCopyInputStream_BufferFlipCallback* callback) {
if (overrun < e->limit) {
// Need to copy remaining data into patch buffer.
UPB_ASSERT(overrun < kUpb_EpsCopyInputStream_SlopBytes);
const char* old_end = ptr;
const char* new_start = &e->patch[0] + overrun;
memset(e->patch + kUpb_EpsCopyInputStream_SlopBytes, 0,
kUpb_EpsCopyInputStream_SlopBytes);
memcpy(e->patch, e->end, kUpb_EpsCopyInputStream_SlopBytes);
ptr = new_start;
e->end = &e->patch[kUpb_EpsCopyInputStream_SlopBytes];
e->limit -= kUpb_EpsCopyInputStream_SlopBytes;
e->limit_ptr = e->end + e->limit;
UPB_ASSERT(ptr < e->limit_ptr);
e->input_delta = (uintptr_t)old_end - (uintptr_t)new_start;
const char* ret = callback(e, old_end, new_start);
if (ret) {
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsChecked)(e);
}
return ret;
} else {
UPB_ASSERT(overrun > e->limit);
e->error = true;
return callback(e, NULL, NULL);
}
}
typedef const char* upb_EpsCopyInputStream_ParseDelimitedFunc(
struct upb_EpsCopyInputStream* e, const char* ptr, int size, void* ctx);

View file

@ -29,11 +29,16 @@ extern "C" {
#endif
UPB_PRIVATE(_upb_WireReader_LongVarint)
UPB_PRIVATE(_upb_WireReader_ReadLongVarint32)(const char* ptr, uint32_t val);
UPB_PRIVATE(_upb_WireReader_ReadLongVarint)(const char* ptr, uint64_t val,
upb_EpsCopyInputStream* stream);
UPB_PRIVATE(_upb_WireReader_LongVarint)
UPB_PRIVATE(_upb_WireReader_ReadLongVarint64)(const char* ptr, uint64_t val);
UPB_PRIVATE(_upb_WireReader_ReadLongTag)(const char* ptr, uint64_t val,
upb_EpsCopyInputStream* stream);
UPB_PRIVATE(_upb_WireReader_LongVarint)
UPB_PRIVATE(_upb_WireReader_ReadLongSize)(const char* ptr, uint64_t val,
upb_EpsCopyInputStream* stream);
UPB_FORCEINLINE const char* UPB_PRIVATE(_upb_WireReader_ReadVarint)(
UPB_FORCEINLINE const char* upb_WireReader_ReadVarint(
const char* ptr, uint64_t* val, upb_EpsCopyInputStream* stream) {
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(stream, 10);
uint8_t byte = *ptr;
@ -41,14 +46,13 @@ UPB_FORCEINLINE const char* UPB_PRIVATE(_upb_WireReader_ReadVarint)(
*val = byte;
return ptr + 1;
}
UPB_PRIVATE(_upb_WireReader_LongVarint)
res = UPB_PRIVATE(_upb_WireReader_ReadLongVarint64)(ptr, byte);
if (UPB_UNLIKELY(!res.ptr)) return NULL;
UPB_PRIVATE(_upb_WireReader_LongVarint) res;
res = UPB_PRIVATE(_upb_WireReader_ReadLongVarint)(ptr, byte, stream);
*val = res.val;
return res.ptr;
return UPB_PRIVATE(upb_EpsCopyInputStream_AssumeResult)(stream, res.ptr);
}
UPB_FORCEINLINE const char* UPB_PRIVATE(_upb_WireReader_ReadTag)(
UPB_FORCEINLINE const char* upb_WireReader_ReadTag(
const char* ptr, uint32_t* val, upb_EpsCopyInputStream* stream) {
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(stream, 5);
uint8_t byte = *ptr;
@ -56,11 +60,24 @@ UPB_FORCEINLINE const char* UPB_PRIVATE(_upb_WireReader_ReadTag)(
*val = byte;
return ptr + 1;
}
UPB_PRIVATE(_upb_WireReader_LongVarint)
res = UPB_PRIVATE(_upb_WireReader_ReadLongVarint32)(ptr, byte);
if (UPB_UNLIKELY(!res.ptr)) return NULL;
UPB_PRIVATE(_upb_WireReader_LongVarint) res;
res = UPB_PRIVATE(_upb_WireReader_ReadLongTag)(ptr, byte, stream);
*val = res.val;
return res.ptr;
return UPB_PRIVATE(upb_EpsCopyInputStream_AssumeResult)(stream, res.ptr);
}
UPB_FORCEINLINE const char* upb_WireReader_ReadSize(
const char* ptr, int* val, upb_EpsCopyInputStream* stream) {
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(stream, 5);
uint8_t byte = *ptr;
if (UPB_LIKELY((byte & 0x80) == 0)) {
*val = byte;
return ptr + 1;
}
UPB_PRIVATE(_upb_WireReader_LongVarint) res;
res = UPB_PRIVATE(_upb_WireReader_ReadLongSize)(ptr, byte, stream);
*val = res.val;
return UPB_PRIVATE(upb_EpsCopyInputStream_AssumeResult)(stream, res.ptr);
}
UPB_API_INLINE uint32_t upb_WireReader_GetFieldNumber(uint32_t tag) {

View file

@ -17,51 +17,64 @@
#include "upb/port/def.inc"
UPB_NOINLINE UPB_PRIVATE(_upb_WireReader_LongVarint)
UPB_PRIVATE(_upb_WireReader_ReadLongVarint64)(const char* ptr,
uint64_t val) {
UPB_PRIVATE(_upb_WireReader_LongVarint) ret = {NULL, 0};
uint64_t byte;
UPB_PRIVATE(_upb_WireReader_ReadLongVarint)(
const char* ptr, uint64_t val, upb_EpsCopyInputStream* stream) {
for (int i = 1; i < 10; i++) {
byte = (uint8_t)ptr[i];
uint64_t byte = (uint8_t)ptr[i];
val += (byte - 1) << (i * 7);
if (!(byte & 0x80)) {
ret.ptr = ptr + i + 1;
ret.val = val;
return ret;
return (UPB_PRIVATE(_upb_WireReader_LongVarint)){ptr + i + 1, val};
}
}
return ret;
return (UPB_PRIVATE(_upb_WireReader_LongVarint)){
UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream), 0};
}
UPB_NOINLINE UPB_PRIVATE(_upb_WireReader_LongVarint)
UPB_PRIVATE(_upb_WireReader_ReadLongVarint32)(const char* ptr,
uint32_t val) {
UPB_PRIVATE(_upb_WireReader_LongVarint) ret = {NULL, 0};
uint64_t byte;
UPB_PRIVATE(_upb_WireReader_ReadLongTag)(const char* ptr, uint64_t val,
upb_EpsCopyInputStream* stream) {
for (int i = 1; i < 5; i++) {
byte = (uint8_t)ptr[i];
uint64_t byte = (uint8_t)ptr[i];
val += (byte - 1) << (i * 7);
if (!(byte & 0x80)) {
ret.ptr = ptr + i + 1;
ret.val = val;
return ret;
if (val > UINT32_MAX) break;
return (UPB_PRIVATE(_upb_WireReader_LongVarint)){ptr + i + 1, val};
}
}
return ret;
return (UPB_PRIVATE(_upb_WireReader_LongVarint)){
UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream), 0};
}
UPB_NOINLINE UPB_PRIVATE(_upb_WireReader_LongVarint)
UPB_PRIVATE(_upb_WireReader_ReadLongSize)(const char* ptr, uint64_t val,
upb_EpsCopyInputStream* stream) {
for (int i = 1; i < 5; i++) {
uint64_t byte = (uint8_t)ptr[i];
val += (byte - 1) << (i * 7);
if (!(byte & 0x80)) {
if (val > INT32_MAX) break;
return (UPB_PRIVATE(_upb_WireReader_LongVarint)){ptr + i + 1, val};
}
}
return (UPB_PRIVATE(_upb_WireReader_LongVarint)){
UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream), 0};
}
const char* UPB_PRIVATE(_upb_WireReader_SkipGroup)(
const char* ptr, uint32_t tag, int depth_limit,
upb_EpsCopyInputStream* stream) {
if (--depth_limit < 0) return NULL;
if (--depth_limit < 0) {
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream);
}
uint32_t end_group_tag = (tag & ~7ULL) | kUpb_WireType_EndGroup;
while (!upb_EpsCopyInputStream_IsDone(stream, &ptr)) {
uint32_t tag;
ptr = upb_WireReader_ReadTag(ptr, &tag, stream);
if (!ptr) return NULL;
if (!ptr) break;
if (tag == end_group_tag) return ptr;
ptr = _upb_WireReader_SkipValue(ptr, tag, depth_limit, stream);
if (!ptr) return NULL;
if (!ptr) break;
}
return NULL; // Encountered limit end before end group tag.
// Encountered limit end before end group tag.
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream);
}

View file

@ -37,9 +37,7 @@ extern "C" {
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
UPB_FORCEINLINE const char* upb_WireReader_ReadTag(
const char* ptr, uint32_t* tag, upb_EpsCopyInputStream* stream) {
return UPB_PRIVATE(_upb_WireReader_ReadTag)(ptr, tag, stream);
}
const char* ptr, uint32_t* tag, upb_EpsCopyInputStream* stream);
// Given a tag, returns the field number.
UPB_API_INLINE uint32_t upb_WireReader_GetFieldNumber(uint32_t tag);
@ -47,10 +45,8 @@ UPB_API_INLINE uint32_t upb_WireReader_GetFieldNumber(uint32_t tag);
// Given a tag, returns the wire type.
UPB_API_INLINE uint8_t upb_WireReader_GetWireType(uint32_t tag);
UPB_INLINE const char* upb_WireReader_ReadVarint(
const char* ptr, uint64_t* val, upb_EpsCopyInputStream* stream) {
return UPB_PRIVATE(_upb_WireReader_ReadVarint)(ptr, val, stream);
}
UPB_FORCEINLINE const char* upb_WireReader_ReadVarint(
const char* ptr, uint64_t* val, upb_EpsCopyInputStream* stream);
// Skips data for a varint, returning a pointer past the end of the varint, or
// NULL if there was an error in the varint data.
@ -71,13 +67,7 @@ UPB_INLINE const char* upb_WireReader_SkipVarint(
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
UPB_INLINE const char* upb_WireReader_ReadSize(const char* ptr, int* size,
upb_EpsCopyInputStream* stream) {
uint64_t size64;
ptr = upb_WireReader_ReadVarint(ptr, &size64, stream);
if (!ptr || size64 >= INT32_MAX) return NULL;
*size = size64;
return ptr;
}
upb_EpsCopyInputStream* stream);
// Reads a fixed32 field, performing byte swapping if necessary.
//
@ -124,7 +114,9 @@ const char* UPB_PRIVATE(_upb_WireReader_SkipGroup)(
// control over this?
UPB_INLINE const char* upb_WireReader_SkipGroup(
const char* ptr, uint32_t tag, upb_EpsCopyInputStream* stream) {
return UPB_PRIVATE(_upb_WireReader_SkipGroup)(ptr, tag, 100, stream);
const char* ret =
UPB_PRIVATE(_upb_WireReader_SkipGroup)(ptr, tag, 100, stream);
return UPB_PRIVATE(upb_EpsCopyInputStream_AssumeResult)(stream, ret);
}
UPB_INLINE const char* _upb_WireReader_SkipValue(
@ -143,7 +135,7 @@ UPB_INLINE const char* _upb_WireReader_SkipValue(
int size;
ptr = upb_WireReader_ReadSize(ptr, &size, stream);
if (!ptr || !upb_EpsCopyInputStream_CheckSize(stream, ptr, size)) {
return NULL;
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream);
}
ptr += size;
return ptr;
@ -152,9 +144,10 @@ UPB_INLINE const char* _upb_WireReader_SkipValue(
return UPB_PRIVATE(_upb_WireReader_SkipGroup)(ptr, tag, depth_limit,
stream);
case kUpb_WireType_EndGroup:
return NULL; // Should be handled before now.
// Should be handled before now.
default:
return NULL; // Unknown wire type.
// Unknown wire type.
return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream);
}
}