WPILibC++ 2027.0.0-alpha-3
Loading...
Searching...
No Matches
eps_copy_input_stream.h
Go to the documentation of this file.
1// Protocol Buffers - Google's data interchange format
2// Copyright 2023 Google LLC. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file or at
6// https://developers.google.com/open-source/licenses/bsd
7
8#ifndef UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
9#define UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
10
11#include <stdint.h>
12#include <string.h>
13
14#include "upb/mem/arena.h"
15
16// Must be last.
17#include "upb/port/def.inc"
18
19#ifdef __cplusplus
20extern "C" {
21#endif
22
23// The maximum number of bytes a single protobuf field can take up in the
24// wire format. We only want to do one bounds check per field, so the input
25// stream guarantees that after upb_EpsCopyInputStream_IsDone() is called,
26// the decoder can read this many bytes without performing another bounds
27// check. The stream will copy into a patch buffer as necessary to guarantee
28// this invariant.
29#define kUpb_EpsCopyInputStream_SlopBytes 16
30
31typedef struct {
32 const char* end; // Can read up to SlopBytes bytes beyond this.
33 const char* limit_ptr; // For bounds checks, = end + UPB_MIN(limit, 0)
34 uintptr_t input_delta; // Diff between the original input pointer and patch
35 const char* buffer_start; // Pointer to the original input buffer
36 int limit; // Submessage limit relative to end
37 bool error; // To distinguish between EOF and error.
41
42// Returns true if the stream is in the error state. A stream enters the error
43// state when the user reads past a limit (caught in IsDone()) or the
44// ZeroCopyInputStream returns an error.
48
50 upb_EpsCopyInputStream* e, const char* old_end, const char* new_start);
51
53 upb_EpsCopyInputStream* e, const char* ptr, int overrun);
54
55// Initializes a upb_EpsCopyInputStream using the contents of the buffer
56// [*ptr, size]. Updates `*ptr` as necessary to guarantee that at least
57// kUpb_EpsCopyInputStream_SlopBytes are available to read.
59 const char** ptr, size_t size,
60 bool enable_aliasing) {
61 e->buffer_start = *ptr;
63 memset(&e->patch, 0, 32);
64 if (size) memcpy(&e->patch, *ptr, size);
65 e->input_delta = (uintptr_t)*ptr - (uintptr_t)e->patch;
66 *ptr = e->patch;
67 e->end = *ptr + size;
68 e->limit = 0;
69 } else {
72 e->input_delta = 0;
73 }
74 e->aliasing = enable_aliasing;
75 e->limit_ptr = e->end;
76 e->error = false;
77}
78
79typedef enum {
80 // The current stream position is at a limit.
82
83 // The current stream position is not at a limit.
85
86 // The current stream position is not at a limit, and the stream needs to
87 // be flipped to a new buffer before more data can be read.
90
91// Returns the status of the current stream position. This is a low-level
92// function, it is simpler to call upb_EpsCopyInputStream_IsDone() if possible.
94 upb_EpsCopyInputStream* e, const char* ptr, int* overrun) {
95 *overrun = ptr - e->end;
96 if (UPB_LIKELY(ptr < e->limit_ptr)) {
98 } else if (UPB_LIKELY(*overrun == e->limit)) {
100 } else {
102 }
103}
104
105// Returns true if the stream has hit a limit, either the current delimited
106// limit or the overall end-of-stream. As a side effect, this function may flip
107// the pointer to a new buffer if there are less than
108// kUpb_EpsCopyInputStream_SlopBytes of data to be read in the current buffer.
109//
110// Postcondition: if the function returns false, there are at least
111// kUpb_EpsCopyInputStream_SlopBytes of data available to read at *ptr.
113 upb_EpsCopyInputStream* e, const char** ptr,
115 int overrun;
116 switch (upb_EpsCopyInputStream_IsDoneStatus(e, *ptr, &overrun)) {
118 return true;
120 return false;
122 *ptr = func(e, *ptr, overrun);
123 return *ptr == NULL;
124 }
126}
127
129 upb_EpsCopyInputStream* e, const char* ptr, int overrun);
130
131// A simpler version of IsDoneWithCallback() that does not support a buffer flip
132// callback. Useful in cases where we do not need to insert custom logic at
133// every buffer flip.
134//
135// If this returns true, the user must call upb_EpsCopyInputStream_IsError()
136// to distinguish between EOF and error.
142
143// Returns the total number of bytes that are safe to read from the current
144// buffer without reading uninitialized or unallocated memory.
145//
146// Note that this check does not respect any semantic limits on the stream,
147// either limits from PushLimit() or the overall stream end, so some of these
148// bytes may have unpredictable, nonsense values in them. The guarantee is only
149// that the bytes are valid to read from the perspective of the C language
150// (ie. you can read without triggering UBSAN or ASAN).
155
156// Returns true if the given delimited field size is valid (it does not extend
157// beyond any previously-pushed limits). `ptr` should point to the beginning
158// of the field data, after the delimited size.
159//
160// Note that this does *not* guarantee that all of the data for this field is in
161// the current buffer.
163 const upb_EpsCopyInputStream* e, const char* ptr, int size) {
164 UPB_ASSERT(size >= 0);
165 return size <= e->limit - (ptr - e->end);
166}
167
169 upb_EpsCopyInputStream* e, const char* ptr, int size, bool submessage) {
170 // This is one extra branch compared to the more normal:
171 // return (size_t)(end - ptr) < size;
172 // However it is one less computation if we are just about to use "ptr + len":
173 // https://godbolt.org/z/35YGPz
174 // In microbenchmarks this shows a small improvement.
175 uintptr_t uptr = (uintptr_t)ptr;
176 uintptr_t uend = (uintptr_t)e->limit_ptr;
177 uintptr_t res = uptr + (size_t)size;
178 if (!submessage) uend += kUpb_EpsCopyInputStream_SlopBytes;
179 // NOTE: this check depends on having a linear address space. This is not
180 // technically guaranteed by uintptr_t.
181 bool ret = res >= uptr && res <= uend;
182 if (size < 0) UPB_ASSERT(!ret);
183 return ret;
184}
185
186// Returns true if the given delimited field size is valid (it does not extend
187// beyond any previously-pushed limited) *and* all of the data for this field is
188// available to be read in the current buffer.
189//
190// If the size is negative, this function will always return false. This
191// property can be useful in some cases.
196
197// Returns true if the given sub-message size is valid (it does not extend
198// beyond any previously-pushed limited) *and* all of the data for this
199// sub-message is available to be parsed in the current buffer.
200//
201// This implies that all fields from the sub-message can be parsed from the
202// current buffer while maintaining the invariant that we always have at least
203// kUpb_EpsCopyInputStream_SlopBytes of data available past the beginning of
204// any individual field start.
205//
206// If the size is negative, this function will always return false. This
207// property can be useful in some cases.
212
213// Returns true if aliasing_enabled=true was passed to
214// upb_EpsCopyInputStream_Init() when this stream was initialized.
219
220// Returns true if aliasing_enabled=true was passed to
221// upb_EpsCopyInputStream_Init() when this stream was initialized *and* we can
222// alias into the region [ptr, size] in an input buffer.
224 upb_EpsCopyInputStream* e, const char* ptr, size_t size) {
225 // When EpsCopyInputStream supports streaming, this will need to become a
226 // runtime check.
227 return e->aliasing &&
229}
230
231// Returns a pointer into an input buffer that corresponds to the parsing
232// pointer `ptr`. The returned pointer may be the same as `ptr`, but also may
233// be different if we are currently parsing out of the patch buffer.
235 upb_EpsCopyInputStream* e, const char* ptr) {
236 // This somewhat silly looking add-and-subtract behavior provides provenance
237 // from the original input buffer's pointer. After optimization it produces
238 // the same assembly as just casting `(uintptr_t)ptr+input_delta`
239 // https://godbolt.org/z/zosG88oPn
240 size_t position =
241 (uintptr_t)ptr + e->input_delta - (uintptr_t)e->buffer_start;
242 return e->buffer_start + position;
243}
244
245// Returns a pointer into an input buffer that corresponds to the parsing
246// pointer `ptr`. The returned pointer may be the same as `ptr`, but also may
247// be different if we are currently parsing out of the patch buffer.
248//
249// REQUIRES: Aliasing must be available for the given pointer. If the input is a
250// flat buffer and aliasing is enabled, then aliasing will always be available.
256
257// Reads string data from the input, aliasing into the input buffer instead of
258// copying. The parsing pointer is passed in `*ptr`, and will be updated if
259// necessary to point to the actual input buffer. Returns the new parsing
260// pointer, which will be advanced past the string data.
261//
262// REQUIRES: Aliasing must be available for this data region (test with
263// upb_EpsCopyInputStream_AliasingAvailable().
265 upb_EpsCopyInputStream* e, const char** ptr, size_t size) {
267 const char* ret = *ptr + size;
269 UPB_ASSUME(ret != NULL);
270 return ret;
271}
272
273// Skips `size` bytes of data from the input and returns a pointer past the end.
274// Returns NULL on end of stream or error.
276 const char* ptr, int size) {
277 if (!upb_EpsCopyInputStream_CheckDataSizeAvailable(e, ptr, size)) return NULL;
278 return ptr + size;
279}
280
281// Copies `size` bytes of data from the input `ptr` into the buffer `to`, and
282// returns a pointer past the end. Returns NULL on end of stream or error.
284 const char* ptr, void* to,
285 int size) {
286 if (!upb_EpsCopyInputStream_CheckDataSizeAvailable(e, ptr, size)) return NULL;
287 memcpy(to, ptr, size);
288 return ptr + size;
289}
290
291// Reads string data from the stream and advances the pointer accordingly.
292// If aliasing was enabled when the stream was initialized, then the returned
293// pointer will point into the input buffer if possible, otherwise new data
294// will be allocated from arena and copied into. We may be forced to copy even
295// if aliasing was enabled if the input data spans input buffers.
296//
297// Returns NULL if memory allocation failed, or we reached a premature EOF.
299 upb_EpsCopyInputStream* e, const char** ptr, size_t size,
300 upb_Arena* arena) {
303 } else {
304 // We need to allocate and copy.
306 return NULL;
307 }
308 UPB_ASSERT(arena);
309 char* data = (char*)upb_Arena_Malloc(arena, size);
310 if (!data) return NULL;
311 const char* ret = upb_EpsCopyInputStream_Copy(e, *ptr, data, size);
312 *ptr = data;
313 return ret;
314 }
315}
316
320
321// Pushes a limit onto the stack of limits for the current stream. The limit
322// will extend for `size` bytes beyond the position in `ptr`. Future calls to
323// upb_EpsCopyInputStream_IsDone() will return `true` when the stream position
324// reaches this limit.
325//
326// Returns a delta that the caller must store and supply to PopLimit() below.
328 const char* ptr, int size) {
329 int limit = size + (int)(ptr - e->end);
330 int delta = e->limit - limit;
332 UPB_ASSERT(limit <= e->limit);
333 e->limit = limit;
334 e->limit_ptr = e->end + UPB_MIN(0, limit);
336 return delta;
337}
338
339// Pops the last limit that was pushed on this stream. This may only be called
340// once IsDone() returns true. The user must pass the delta that was returned
341// from PushLimit().
343 const char* ptr,
344 int saved_delta) {
345 UPB_ASSERT(ptr - e->end == e->limit);
347 e->limit += saved_delta;
348 e->limit_ptr = e->end + UPB_MIN(0, e->limit);
350}
351
353 upb_EpsCopyInputStream* e, const char* ptr, int overrun,
355 if (overrun < e->limit) {
356 // Need to copy remaining data into patch buffer.
358 const char* old_end = ptr;
359 const char* new_start = &e->patch[0] + overrun;
363 ptr = new_start;
366 e->limit_ptr = e->end + e->limit;
367 UPB_ASSERT(ptr < e->limit_ptr);
368 e->input_delta = (uintptr_t)old_end - (uintptr_t)new_start;
369 return callback(e, old_end, new_start);
370 } else {
371 UPB_ASSERT(overrun > e->limit);
372 e->error = true;
373 return callback(e, NULL, NULL);
374 }
375}
376
378 upb_EpsCopyInputStream* e, const char* ptr, void* ctx);
379
380// Tries to perform a fast-path handling of the given delimited message data.
381// If the sub-message beginning at `*ptr` and extending for `len` is short and
382// fits within this buffer, calls `func` with `ctx` as a parameter, where the
383// pushing and popping of limits is handled automatically and with lower cost
384// than the normal PushLimit()/PopLimit() sequence.
386 upb_EpsCopyInputStream* e, const char** ptr, int len,
389 return false;
390 }
391
392 // Fast case: Sub-message is <128 bytes and fits in the current buffer.
393 // This means we can preserve limit/limit_ptr verbatim.
394 const char* saved_limit_ptr = e->limit_ptr;
395 int saved_limit = e->limit;
396 e->limit_ptr = *ptr + len;
397 e->limit = e->limit_ptr - e->end;
398 UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
399 *ptr = func(e, *ptr, ctx);
400 e->limit_ptr = saved_limit_ptr;
401 e->limit = saved_limit;
402 UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
403 return true;
404}
405
406#ifdef __cplusplus
407} /* extern "C" */
408#endif
409
410#include "upb/port/undef.inc"
411
412#endif // UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
#define UPB_UNREACHABLE()
Definition def.inc:345
#define UPB_LIKELY(x)
Definition def.inc:264
#define UPB_ASSUME(expr)
Definition def.inc:319
#define UPB_ASSERT(expr)
Definition def.inc:329
#define UPB_FORCEINLINE
Definition def.inc:288
#define UPB_INLINE
Definition def.inc:144
#define UPB_MIN(x, y)
Definition def.inc:301
UPB_INLINE const char * upb_EpsCopyInputStream_Skip(upb_EpsCopyInputStream *e, const char *ptr, int size)
Definition eps_copy_input_stream.h:275
UPB_INLINE bool upb_EpsCopyInputStream_CheckDataSizeAvailable(upb_EpsCopyInputStream *e, const char *ptr, int size)
Definition eps_copy_input_stream.h:192
UPB_INLINE const char * upb_EpsCopyInputStream_GetInputPtr(upb_EpsCopyInputStream *e, const char *ptr)
Definition eps_copy_input_stream.h:234
UPB_INLINE const char * upb_EpsCopyInputStream_ReadStringAliased(upb_EpsCopyInputStream *e, const char **ptr, size_t size)
Definition eps_copy_input_stream.h:264
UPB_INLINE const char * upb_EpsCopyInputStream_GetAliasedPtr(upb_EpsCopyInputStream *e, const char *ptr)
Definition eps_copy_input_stream.h:251
UPB_INLINE const char * _upb_EpsCopyInputStream_IsDoneFallbackInline(upb_EpsCopyInputStream *e, const char *ptr, int overrun, upb_EpsCopyInputStream_BufferFlipCallback *callback)
Definition eps_copy_input_stream.h:352
UPB_FORCEINLINE bool upb_EpsCopyInputStream_TryParseDelimitedFast(upb_EpsCopyInputStream *e, const char **ptr, int len, upb_EpsCopyInputStream_ParseDelimitedFunc *func, void *ctx)
Definition eps_copy_input_stream.h:385
UPB_INLINE bool upb_EpsCopyInputStream_IsDoneWithCallback(upb_EpsCopyInputStream *e, const char **ptr, upb_EpsCopyInputStream_IsDoneFallbackFunc *func)
Definition eps_copy_input_stream.h:112
UPB_INLINE bool upb_EpsCopyInputStream_AliasingAvailable(upb_EpsCopyInputStream *e, const char *ptr, size_t size)
Definition eps_copy_input_stream.h:223
UPB_INLINE bool upb_EpsCopyInputStream_AliasingEnabled(upb_EpsCopyInputStream *e)
Definition eps_copy_input_stream.h:215
UPB_INLINE upb_IsDoneStatus upb_EpsCopyInputStream_IsDoneStatus(upb_EpsCopyInputStream *e, const char *ptr, int *overrun)
Definition eps_copy_input_stream.h:93
UPB_INLINE const char * upb_EpsCopyInputStream_Copy(upb_EpsCopyInputStream *e, const char *ptr, void *to, int size)
Definition eps_copy_input_stream.h:283
UPB_INLINE bool upb_EpsCopyInputStream_IsError(upb_EpsCopyInputStream *e)
Definition eps_copy_input_stream.h:45
UPB_INLINE bool upb_EpsCopyInputStream_CheckSubMessageSizeAvailable(upb_EpsCopyInputStream *e, const char *ptr, int size)
Definition eps_copy_input_stream.h:208
UPB_INLINE void upb_EpsCopyInputStream_Init(upb_EpsCopyInputStream *e, const char **ptr, size_t size, bool enable_aliasing)
Definition eps_copy_input_stream.h:58
UPB_INLINE const char * upb_EpsCopyInputStream_ReadString(upb_EpsCopyInputStream *e, const char **ptr, size_t size, upb_Arena *arena)
Definition eps_copy_input_stream.h:298
UPB_INLINE void _upb_EpsCopyInputStream_CheckLimit(upb_EpsCopyInputStream *e)
Definition eps_copy_input_stream.h:317
UPB_INLINE int upb_EpsCopyInputStream_PushLimit(upb_EpsCopyInputStream *e, const char *ptr, int size)
Definition eps_copy_input_stream.h:327
const char * upb_EpsCopyInputStream_BufferFlipCallback(upb_EpsCopyInputStream *e, const char *old_end, const char *new_start)
Definition eps_copy_input_stream.h:49
upb_IsDoneStatus
Definition eps_copy_input_stream.h:79
@ kUpb_IsDoneStatus_NotDone
Definition eps_copy_input_stream.h:84
@ kUpb_IsDoneStatus_Done
Definition eps_copy_input_stream.h:81
@ kUpb_IsDoneStatus_NeedFallback
Definition eps_copy_input_stream.h:88
const char * _upb_EpsCopyInputStream_IsDoneFallbackNoCallback(upb_EpsCopyInputStream *e, const char *ptr, int overrun)
#define kUpb_EpsCopyInputStream_SlopBytes
Definition eps_copy_input_stream.h:29
const char * upb_EpsCopyInputStream_ParseDelimitedFunc(upb_EpsCopyInputStream *e, const char *ptr, void *ctx)
Definition eps_copy_input_stream.h:377
UPB_INLINE bool upb_EpsCopyInputStream_IsDone(upb_EpsCopyInputStream *e, const char **ptr)
Definition eps_copy_input_stream.h:137
const char * upb_EpsCopyInputStream_IsDoneFallbackFunc(upb_EpsCopyInputStream *e, const char *ptr, int overrun)
Definition eps_copy_input_stream.h:52
UPB_INLINE void upb_EpsCopyInputStream_PopLimit(upb_EpsCopyInputStream *e, const char *ptr, int saved_delta)
Definition eps_copy_input_stream.h:342
UPB_INLINE bool _upb_EpsCopyInputStream_CheckSizeAvailable(upb_EpsCopyInputStream *e, const char *ptr, int size, bool submessage)
Definition eps_copy_input_stream.h:168
UPB_INLINE size_t upb_EpsCopyInputStream_BytesAvailable(upb_EpsCopyInputStream *e, const char *ptr)
Definition eps_copy_input_stream.h:151
UPB_INLINE bool upb_EpsCopyInputStream_CheckSize(const upb_EpsCopyInputStream *e, const char *ptr, int size)
Definition eps_copy_input_stream.h:162
auto ptr(T p) -> const void *
Converts p to const void* for pointer formatting.
Definition format.h:3963
UPB_API_INLINE void * upb_Arena_Malloc(struct upb_Arena *a, size_t size)
Definition arena.h:65
Definition arena.h:29
Definition eps_copy_input_stream.h:31
char patch[kUpb_EpsCopyInputStream_SlopBytes *2]
Definition eps_copy_input_stream.h:39
bool error
Definition eps_copy_input_stream.h:37
bool aliasing
Definition eps_copy_input_stream.h:38
const char * end
Definition eps_copy_input_stream.h:32
const char * limit_ptr
Definition eps_copy_input_stream.h:33
int limit
Definition eps_copy_input_stream.h:36
uintptr_t input_delta
Definition eps_copy_input_stream.h:34
const char * buffer_start
Definition eps_copy_input_stream.h:35