|
|
|
/*
|
|
|
|
* Copyright (c) 2009-2021, Google LLC
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Google LLC nor the
|
|
|
|
* names of its contributors may be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
|
|
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
|
|
|
|
#define UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
// Must be last.
|
|
|
|
#include "upb/port/def.inc"
|
|
|
|
|
|
|
|
// The maximum number of bytes a single protobuf field can take up in the
|
|
|
|
// wire format. We only want to do one bounds check per field, so the input
|
|
|
|
// stream guarantees that after upb_EpsCopyInputStream_IsDone() is called,
|
|
|
|
// the decoder can read this many bytes without performing another bounds
|
|
|
|
// check. The stream will copy into a patch buffer as necessary to guarantee
|
|
|
|
// this invariant.
|
|
|
|
#define kUpb_EpsCopyInputStream_SlopBytes 16
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
const char* end; // Can read up to SlopBytes bytes beyond this.
|
|
|
|
const char* limit_ptr; // For bounds checks, = end + UPB_MIN(limit, 0)
|
|
|
|
int limit; // Submessage limit relative to end
|
|
|
|
char patch[kUpb_EpsCopyInputStream_SlopBytes * 2];
|
|
|
|
} upb_EpsCopyInputStream;
|
|
|
|
|
|
|
|
typedef const char* upb_EpsCopyInputStream_BufferFlipCallback(
|
|
|
|
upb_EpsCopyInputStream* e, const char* old_end, const char* new_start);
|
|
|
|
|
|
|
|
typedef const char* upb_EpsCopyInputStream_IsDoneFallbackFunc(
|
|
|
|
upb_EpsCopyInputStream* e, const char* ptr, int overrun);
|
|
|
|
|
|
|
|
// Initializes a upb_EpsCopyInputStream using the contents of the buffer
|
|
|
|
// [*ptr, size]. Updates `*ptr` as necessary to guarantee that at least
|
|
|
|
// kUpb_EpsCopyInputStream_SlopBytes, and returns true if the pointer has been
|
|
|
|
// updated.
|
|
|
|
UPB_INLINE bool upb_EpsCopyInputStream_Init(upb_EpsCopyInputStream* e,
|
|
|
|
const char** ptr, size_t size) {
|
|
|
|
bool ret;
|
|
|
|
if (size <= kUpb_EpsCopyInputStream_SlopBytes) {
|
|
|
|
memset(&e->patch, 0, 32);
|
|
|
|
if (size) memcpy(&e->patch, *ptr, size);
|
|
|
|
*ptr = e->patch;
|
|
|
|
e->end = *ptr + size;
|
|
|
|
e->limit = 0;
|
|
|
|
ret = true;
|
|
|
|
} else {
|
|
|
|
e->end = *ptr + size - kUpb_EpsCopyInputStream_SlopBytes;
|
|
|
|
e->limit = kUpb_EpsCopyInputStream_SlopBytes;
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
e->limit_ptr = e->end;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
// The current stream position is at a limit.
|
|
|
|
kUpb_IsDoneStatus_Done,
|
|
|
|
|
|
|
|
// The current stream position is not at a limit.
|
|
|
|
kUpb_IsDoneStatus_NotDone,
|
|
|
|
|
|
|
|
// The current stream position is not at a limit, and the stream needs to
|
|
|
|
// be flipped to a new buffer before more data can be read.
|
|
|
|
kUpb_IsDoneStatus_NeedFallback,
|
|
|
|
} upb_IsDoneStatus;
|
|
|
|
|
|
|
|
// Returns the status of the current stream position. This is a low-level
|
|
|
|
// function, it is simpler to call upb_EpsCopyInputStream_IsDone() if possible.
|
|
|
|
UPB_INLINE upb_IsDoneStatus upb_EpsCopyInputStream_IsDoneStatus(
|
|
|
|
upb_EpsCopyInputStream* e, const char* ptr, int* overrun) {
|
|
|
|
*overrun = ptr - e->end;
|
|
|
|
if (UPB_LIKELY(ptr < e->limit_ptr)) {
|
|
|
|
return kUpb_IsDoneStatus_NotDone;
|
|
|
|
} else if (UPB_LIKELY(*overrun == e->limit)) {
|
|
|
|
return kUpb_IsDoneStatus_Done;
|
|
|
|
} else {
|
|
|
|
return kUpb_IsDoneStatus_NeedFallback;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the stream has hit a limit, either the current delimited
|
|
|
|
// limit or the overall end-of-stream. As a side effect, this function may flip
|
|
|
|
// the pointer to a new buffer if there are less than
|
|
|
|
// kUpb_EpsCopyInputStream_SlopBytes of data to be read in the current buffer.
|
|
|
|
//
|
|
|
|
// Postcondition: if the function returns false, there are at least
|
|
|
|
// kUpb_EpsCopyInputStream_SlopBytes of data available to read at *ptr.
|
|
|
|
UPB_INLINE bool upb_EpsCopyInputStream_IsDone(
|
|
|
|
upb_EpsCopyInputStream* e, const char** ptr,
|
|
|
|
upb_EpsCopyInputStream_IsDoneFallbackFunc* func) {
|
|
|
|
int overrun;
|
|
|
|
switch (upb_EpsCopyInputStream_IsDoneStatus(e, *ptr, &overrun)) {
|
|
|
|
case kUpb_IsDoneStatus_Done:
|
|
|
|
return true;
|
|
|
|
case kUpb_IsDoneStatus_NotDone:
|
|
|
|
return false;
|
|
|
|
case kUpb_IsDoneStatus_NeedFallback:
|
|
|
|
*ptr = func(e, *ptr, overrun);
|
|
|
|
return *ptr == NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the total number of bytes that are safe to read from the current
|
|
|
|
// buffer without reading uninitialized or unallocated memory.
|
|
|
|
//
|
|
|
|
// Note that this check does not respect any semantic limits on the stream,
|
|
|
|
// either limits from PushLimit() or the overall stream end, so some of these
|
|
|
|
// bytes may have unpredictable, nonsense values in them. The guarantee is only
|
|
|
|
// that the bytes are valid to read from the perspective of the C language
|
|
|
|
// (ie. you can read without triggering UBSAN or ASAN).
|
|
|
|
UPB_INLINE size_t upb_EpsCopyInputStream_BytesAvailable(
|
|
|
|
upb_EpsCopyInputStream* e, const char* ptr) {
|
|
|
|
return (e->end - ptr) + kUpb_EpsCopyInputStream_SlopBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the given delimited field size is valid (it does not extend
|
|
|
|
// beyond any previously-pushed limits). `ptr` should point to the beginning
|
|
|
|
// of the field data, after the delimited size.
|
|
|
|
//
|
|
|
|
// Note that this does *not* guarantee that all of the data for this field is in
|
|
|
|
// the current buffer.
|
|
|
|
UPB_INLINE bool upb_EpsCopyInputStream_CheckSize(
|
|
|
|
const upb_EpsCopyInputStream* e, const char* ptr, int size) {
|
|
|
|
UPB_ASSERT(size >= 0);
|
|
|
|
return ptr - e->end + size <= e->limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool _upb_EpsCopyInputStream_CheckSizeAvailable(
|
|
|
|
upb_EpsCopyInputStream* e, const char* ptr, int size, bool submessage) {
|
|
|
|
// This is one extra branch compared to the more normal:
|
|
|
|
// return (size_t)(end - ptr) < size;
|
|
|
|
// However it is one less computation if we are just about to use "ptr + len":
|
|
|
|
// https://godbolt.org/z/35YGPz
|
|
|
|
// In microbenchmarks this shows a small improvement.
|
|
|
|
uintptr_t uptr = (uintptr_t)ptr;
|
|
|
|
uintptr_t uend = (uintptr_t)e->limit_ptr;
|
|
|
|
uintptr_t res = uptr + (size_t)size;
|
|
|
|
if (!submessage) uend += kUpb_EpsCopyInputStream_SlopBytes;
|
|
|
|
// NOTE: this check depends on having a linear address space. This is not
|
|
|
|
// technically guaranteed by uintptr_t.
|
|
|
|
bool ret = res >= uptr && res <= uend;
|
|
|
|
if (size < 0) UPB_ASSERT(!ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the given delimited field size is valid (it does not extend
|
|
|
|
// beyond any previously-pushed limited) *and* all of the data for this field is
|
|
|
|
// available to be read in the current buffer.
|
|
|
|
//
|
|
|
|
// If the size is negative, this function will always return false. This
|
|
|
|
// property can be useful in some cases.
|
|
|
|
UPB_INLINE bool upb_EpsCopyInputStream_CheckDataSizeAvailable(
|
|
|
|
upb_EpsCopyInputStream* e, const char* ptr, int size) {
|
|
|
|
return _upb_EpsCopyInputStream_CheckSizeAvailable(e, ptr, size, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the given sub-message size is valid (it does not extend
|
|
|
|
// beyond any previously-pushed limited) *and* all of the data for this
|
|
|
|
// sub-message is available to be parsed in the current buffer.
|
|
|
|
//
|
|
|
|
// This implies that all fields from the sub-message can be parsed from the
|
|
|
|
// current buffer while maintaining the invariant that we always have at least
|
|
|
|
// kUpb_EpsCopyInputStream_SlopBytes of data available past the beginning of
|
|
|
|
// any individual field start.
|
|
|
|
//
|
|
|
|
// If the size is negative, this function will always return false. This
|
|
|
|
// property can be useful in some cases.
|
|
|
|
UPB_INLINE bool upb_EpsCopyInputStream_CheckSubMessageSizeAvailable(
|
|
|
|
upb_EpsCopyInputStream* e, const char* ptr, int size) {
|
|
|
|
return _upb_EpsCopyInputStream_CheckSizeAvailable(e, ptr, size, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void _upb_EpsCopyInputStream_CheckLimit(upb_EpsCopyInputStream* e) {
|
|
|
|
UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pushes a limit onto the stack of limits for the current stream. The limit
|
|
|
|
// will extend for `size` bytes beyond the position in `ptr`. Future calls to
|
|
|
|
// upb_EpsCopyInputStream_IsDone() will return `true` when the stream position
|
|
|
|
// reaches this limit.
|
|
|
|
//
|
|
|
|
// Returns a delta that the caller must store and supply to PopLimit() below.
|
|
|
|
UPB_INLINE int upb_EpsCopyInputStream_PushLimit(upb_EpsCopyInputStream* e,
|
|
|
|
const char* ptr, int size) {
|
|
|
|
int limit = size + (int)(ptr - e->end);
|
|
|
|
int delta = e->limit - limit;
|
|
|
|
_upb_EpsCopyInputStream_CheckLimit(e);
|
|
|
|
e->limit = limit;
|
|
|
|
e->limit_ptr = e->end + UPB_MIN(0, limit);
|
|
|
|
_upb_EpsCopyInputStream_CheckLimit(e);
|
|
|
|
return delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pops the last limit that was pushed on this stream. This may only be called
|
|
|
|
// once IsDone() returns true. The user must pass the delta that was returned
|
|
|
|
// from PushLimit().
|
|
|
|
UPB_INLINE void upb_EpsCopyInputStream_PopLimit(upb_EpsCopyInputStream* e,
|
|
|
|
const char* ptr,
|
|
|
|
int saved_delta) {
|
|
|
|
UPB_ASSERT(ptr - e->end == e->limit);
|
|
|
|
_upb_EpsCopyInputStream_CheckLimit(e);
|
|
|
|
e->limit += saved_delta;
|
|
|
|
e->limit_ptr = e->end + UPB_MIN(0, e->limit);
|
|
|
|
_upb_EpsCopyInputStream_CheckLimit(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE const char* _upb_EpsCopyInputStream_IsDoneFallbackInline(
|
|
|
|
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);
|
|
|
|
return callback(e, old_end, new_start);
|
|
|
|
} else {
|
|
|
|
return callback(e, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef const char* upb_EpsCopyInputStream_ParseDelimitedFunc(
|
|
|
|
upb_EpsCopyInputStream* e, const char* ptr, void* ctx);
|
|
|
|
|
|
|
|
// Tries to perform a fast-path handling of the given delimited message data.
|
|
|
|
// If the sub-message beginning at `*ptr` and extending for `len` is short and
|
|
|
|
// fits within this buffer, calls `func` with `ctx` as a parameter, where the
|
|
|
|
// pushing and popping of limits is handled automatically and with lower cost
|
|
|
|
// than the normal PushLimit()/PopLimit() sequence.
|
|
|
|
static UPB_FORCEINLINE bool upb_EpsCopyInputStream_TryParseDelimitedFast(
|
|
|
|
upb_EpsCopyInputStream* e, const char** ptr, int len,
|
|
|
|
upb_EpsCopyInputStream_ParseDelimitedFunc* func, void* ctx) {
|
|
|
|
if (!upb_EpsCopyInputStream_CheckSubMessageSizeAvailable(e, *ptr, len)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fast case: Sub-message is <128 bytes and fits in the current buffer.
|
|
|
|
// This means we can preserve limit/limit_ptr verbatim.
|
|
|
|
const char* saved_limit_ptr = e->limit_ptr;
|
|
|
|
int saved_limit = e->limit;
|
|
|
|
e->limit_ptr = *ptr + len;
|
|
|
|
e->limit = e->limit_ptr - e->end;
|
|
|
|
UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
|
|
|
|
*ptr = func(e, *ptr, ctx);
|
|
|
|
e->limit_ptr = saved_limit_ptr;
|
|
|
|
e->limit = saved_limit;
|
|
|
|
UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "upb/port/undef.inc"
|
|
|
|
|
|
|
|
#endif // UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
|