Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-11 09:41:46

0001 // Protocol Buffers - Google's data interchange format
0002 // Copyright 2023 Google LLC.  All rights reserved.
0003 //
0004 // Use of this source code is governed by a BSD-style
0005 // license that can be found in the LICENSE file or at
0006 // https://developers.google.com/open-source/licenses/bsd
0007 
0008 #ifndef UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
0009 #define UPB_WIRE_EPS_COPY_INPUT_STREAM_H_
0010 
0011 #include <stdint.h>
0012 #include <string.h>
0013 
0014 #include "upb/mem/arena.h"
0015 
0016 // Must be last.
0017 #include "upb/port/def.inc"
0018 
0019 #ifdef __cplusplus
0020 extern "C" {
0021 #endif
0022 
0023 // The maximum number of bytes a single protobuf field can take up in the
0024 // wire format.  We only want to do one bounds check per field, so the input
0025 // stream guarantees that after upb_EpsCopyInputStream_IsDone() is called,
0026 // the decoder can read this many bytes without performing another bounds
0027 // check.  The stream will copy into a patch buffer as necessary to guarantee
0028 // this invariant.
0029 #define kUpb_EpsCopyInputStream_SlopBytes 16
0030 
0031 typedef struct {
0032   const char* end;        // Can read up to SlopBytes bytes beyond this.
0033   const char* limit_ptr;  // For bounds checks, = end + UPB_MIN(limit, 0)
0034   uintptr_t input_delta;  // Diff between the original input pointer and patch
0035   const char* buffer_start;  // Pointer to the original input buffer
0036   int limit;                 // Submessage limit relative to end
0037   bool error;                // To distinguish between EOF and error.
0038   bool aliasing;
0039   char patch[kUpb_EpsCopyInputStream_SlopBytes * 2];
0040 } upb_EpsCopyInputStream;
0041 
0042 // Returns true if the stream is in the error state. A stream enters the error
0043 // state when the user reads past a limit (caught in IsDone()) or the
0044 // ZeroCopyInputStream returns an error.
0045 UPB_INLINE bool upb_EpsCopyInputStream_IsError(upb_EpsCopyInputStream* e) {
0046   return e->error;
0047 }
0048 
0049 typedef const char* upb_EpsCopyInputStream_BufferFlipCallback(
0050     upb_EpsCopyInputStream* e, const char* old_end, const char* new_start);
0051 
0052 typedef const char* upb_EpsCopyInputStream_IsDoneFallbackFunc(
0053     upb_EpsCopyInputStream* e, const char* ptr, int overrun);
0054 
0055 // Initializes a upb_EpsCopyInputStream using the contents of the buffer
0056 // [*ptr, size].  Updates `*ptr` as necessary to guarantee that at least
0057 // kUpb_EpsCopyInputStream_SlopBytes are available to read.
0058 UPB_INLINE void upb_EpsCopyInputStream_Init(upb_EpsCopyInputStream* e,
0059                                             const char** ptr, size_t size,
0060                                             bool enable_aliasing) {
0061   e->buffer_start = *ptr;
0062   if (size <= kUpb_EpsCopyInputStream_SlopBytes) {
0063     memset(&e->patch, 0, 32);
0064     if (size) memcpy(&e->patch, *ptr, size);
0065     e->input_delta = (uintptr_t)*ptr - (uintptr_t)e->patch;
0066     *ptr = e->patch;
0067     e->end = *ptr + size;
0068     e->limit = 0;
0069   } else {
0070     e->end = *ptr + size - kUpb_EpsCopyInputStream_SlopBytes;
0071     e->limit = kUpb_EpsCopyInputStream_SlopBytes;
0072     e->input_delta = 0;
0073   }
0074   e->aliasing = enable_aliasing;
0075   e->limit_ptr = e->end;
0076   e->error = false;
0077 }
0078 
0079 typedef enum {
0080   // The current stream position is at a limit.
0081   kUpb_IsDoneStatus_Done,
0082 
0083   // The current stream position is not at a limit.
0084   kUpb_IsDoneStatus_NotDone,
0085 
0086   // The current stream position is not at a limit, and the stream needs to
0087   // be flipped to a new buffer before more data can be read.
0088   kUpb_IsDoneStatus_NeedFallback,
0089 } upb_IsDoneStatus;
0090 
0091 // Returns the status of the current stream position.  This is a low-level
0092 // function, it is simpler to call upb_EpsCopyInputStream_IsDone() if possible.
0093 UPB_INLINE upb_IsDoneStatus upb_EpsCopyInputStream_IsDoneStatus(
0094     upb_EpsCopyInputStream* e, const char* ptr, int* overrun) {
0095   *overrun = ptr - e->end;
0096   if (UPB_LIKELY(ptr < e->limit_ptr)) {
0097     return kUpb_IsDoneStatus_NotDone;
0098   } else if (UPB_LIKELY(*overrun == e->limit)) {
0099     return kUpb_IsDoneStatus_Done;
0100   } else {
0101     return kUpb_IsDoneStatus_NeedFallback;
0102   }
0103 }
0104 
0105 // Returns true if the stream has hit a limit, either the current delimited
0106 // limit or the overall end-of-stream. As a side effect, this function may flip
0107 // the pointer to a new buffer if there are less than
0108 // kUpb_EpsCopyInputStream_SlopBytes of data to be read in the current buffer.
0109 //
0110 // Postcondition: if the function returns false, there are at least
0111 // kUpb_EpsCopyInputStream_SlopBytes of data available to read at *ptr.
0112 UPB_INLINE bool upb_EpsCopyInputStream_IsDoneWithCallback(
0113     upb_EpsCopyInputStream* e, const char** ptr,
0114     upb_EpsCopyInputStream_IsDoneFallbackFunc* func) {
0115   int overrun;
0116   switch (upb_EpsCopyInputStream_IsDoneStatus(e, *ptr, &overrun)) {
0117     case kUpb_IsDoneStatus_Done:
0118       return true;
0119     case kUpb_IsDoneStatus_NotDone:
0120       return false;
0121     case kUpb_IsDoneStatus_NeedFallback:
0122       *ptr = func(e, *ptr, overrun);
0123       return *ptr == NULL;
0124   }
0125   UPB_UNREACHABLE();
0126 }
0127 
0128 const char* _upb_EpsCopyInputStream_IsDoneFallbackNoCallback(
0129     upb_EpsCopyInputStream* e, const char* ptr, int overrun);
0130 
0131 // A simpler version of IsDoneWithCallback() that does not support a buffer flip
0132 // callback. Useful in cases where we do not need to insert custom logic at
0133 // every buffer flip.
0134 //
0135 // If this returns true, the user must call upb_EpsCopyInputStream_IsError()
0136 // to distinguish between EOF and error.
0137 UPB_INLINE bool upb_EpsCopyInputStream_IsDone(upb_EpsCopyInputStream* e,
0138                                               const char** ptr) {
0139   return upb_EpsCopyInputStream_IsDoneWithCallback(
0140       e, ptr, _upb_EpsCopyInputStream_IsDoneFallbackNoCallback);
0141 }
0142 
0143 // Returns the total number of bytes that are safe to read from the current
0144 // buffer without reading uninitialized or unallocated memory.
0145 //
0146 // Note that this check does not respect any semantic limits on the stream,
0147 // either limits from PushLimit() or the overall stream end, so some of these
0148 // bytes may have unpredictable, nonsense values in them. The guarantee is only
0149 // that the bytes are valid to read from the perspective of the C language
0150 // (ie. you can read without triggering UBSAN or ASAN).
0151 UPB_INLINE size_t upb_EpsCopyInputStream_BytesAvailable(
0152     upb_EpsCopyInputStream* e, const char* ptr) {
0153   return (e->end - ptr) + kUpb_EpsCopyInputStream_SlopBytes;
0154 }
0155 
0156 // Returns true if the given delimited field size is valid (it does not extend
0157 // beyond any previously-pushed limits).  `ptr` should point to the beginning
0158 // of the field data, after the delimited size.
0159 //
0160 // Note that this does *not* guarantee that all of the data for this field is in
0161 // the current buffer.
0162 UPB_INLINE bool upb_EpsCopyInputStream_CheckSize(
0163     const upb_EpsCopyInputStream* e, const char* ptr, int size) {
0164   UPB_ASSERT(size >= 0);
0165   return size <= e->limit - (ptr - e->end);
0166 }
0167 
0168 UPB_INLINE bool _upb_EpsCopyInputStream_CheckSizeAvailable(
0169     upb_EpsCopyInputStream* e, const char* ptr, int size, bool submessage) {
0170   // This is one extra branch compared to the more normal:
0171   //   return (size_t)(end - ptr) < size;
0172   // However it is one less computation if we are just about to use "ptr + len":
0173   //   https://godbolt.org/z/35YGPz
0174   // In microbenchmarks this shows a small improvement.
0175   uintptr_t uptr = (uintptr_t)ptr;
0176   uintptr_t uend = (uintptr_t)e->limit_ptr;
0177   uintptr_t res = uptr + (size_t)size;
0178   if (!submessage) uend += kUpb_EpsCopyInputStream_SlopBytes;
0179   // NOTE: this check depends on having a linear address space.  This is not
0180   // technically guaranteed by uintptr_t.
0181   bool ret = res >= uptr && res <= uend;
0182   if (size < 0) UPB_ASSERT(!ret);
0183   return ret;
0184 }
0185 
0186 // Returns true if the given delimited field size is valid (it does not extend
0187 // beyond any previously-pushed limited) *and* all of the data for this field is
0188 // available to be read in the current buffer.
0189 //
0190 // If the size is negative, this function will always return false. This
0191 // property can be useful in some cases.
0192 UPB_INLINE bool upb_EpsCopyInputStream_CheckDataSizeAvailable(
0193     upb_EpsCopyInputStream* e, const char* ptr, int size) {
0194   return _upb_EpsCopyInputStream_CheckSizeAvailable(e, ptr, size, false);
0195 }
0196 
0197 // Returns true if the given sub-message size is valid (it does not extend
0198 // beyond any previously-pushed limited) *and* all of the data for this
0199 // sub-message is available to be parsed in the current buffer.
0200 //
0201 // This implies that all fields from the sub-message can be parsed from the
0202 // current buffer while maintaining the invariant that we always have at least
0203 // kUpb_EpsCopyInputStream_SlopBytes of data available past the beginning of
0204 // any individual field start.
0205 //
0206 // If the size is negative, this function will always return false. This
0207 // property can be useful in some cases.
0208 UPB_INLINE bool upb_EpsCopyInputStream_CheckSubMessageSizeAvailable(
0209     upb_EpsCopyInputStream* e, const char* ptr, int size) {
0210   return _upb_EpsCopyInputStream_CheckSizeAvailable(e, ptr, size, true);
0211 }
0212 
0213 // Returns true if aliasing_enabled=true was passed to
0214 // upb_EpsCopyInputStream_Init() when this stream was initialized.
0215 UPB_INLINE bool upb_EpsCopyInputStream_AliasingEnabled(
0216     upb_EpsCopyInputStream* e) {
0217   return e->aliasing;
0218 }
0219 
0220 // Returns true if aliasing_enabled=true was passed to
0221 // upb_EpsCopyInputStream_Init() when this stream was initialized *and* we can
0222 // alias into the region [ptr, size] in an input buffer.
0223 UPB_INLINE bool upb_EpsCopyInputStream_AliasingAvailable(
0224     upb_EpsCopyInputStream* e, const char* ptr, size_t size) {
0225   // When EpsCopyInputStream supports streaming, this will need to become a
0226   // runtime check.
0227   return e->aliasing &&
0228          upb_EpsCopyInputStream_CheckDataSizeAvailable(e, ptr, size);
0229 }
0230 
0231 // Returns a pointer into an input buffer that corresponds to the parsing
0232 // pointer `ptr`.  The returned pointer may be the same as `ptr`, but also may
0233 // be different if we are currently parsing out of the patch buffer.
0234 UPB_INLINE const char* upb_EpsCopyInputStream_GetInputPtr(
0235     upb_EpsCopyInputStream* e, const char* ptr) {
0236   // This somewhat silly looking add-and-subtract behavior provides provenance
0237   // from the original input buffer's pointer. After optimization it produces
0238   // the same assembly as just casting `(uintptr_t)ptr+input_delta`
0239   // https://godbolt.org/z/zosG88oPn
0240   size_t position =
0241       (uintptr_t)ptr + e->input_delta - (uintptr_t)e->buffer_start;
0242   return e->buffer_start + position;
0243 }
0244 
0245 // Returns a pointer into an input buffer that corresponds to the parsing
0246 // pointer `ptr`.  The returned pointer may be the same as `ptr`, but also may
0247 // be different if we are currently parsing out of the patch buffer.
0248 //
0249 // REQUIRES: Aliasing must be available for the given pointer. If the input is a
0250 // flat buffer and aliasing is enabled, then aliasing will always be available.
0251 UPB_INLINE const char* upb_EpsCopyInputStream_GetAliasedPtr(
0252     upb_EpsCopyInputStream* e, const char* ptr) {
0253   UPB_ASSUME(upb_EpsCopyInputStream_AliasingAvailable(e, ptr, 0));
0254   return upb_EpsCopyInputStream_GetInputPtr(e, ptr);
0255 }
0256 
0257 // Reads string data from the input, aliasing into the input buffer instead of
0258 // copying. The parsing pointer is passed in `*ptr`, and will be updated if
0259 // necessary to point to the actual input buffer. Returns the new parsing
0260 // pointer, which will be advanced past the string data.
0261 //
0262 // REQUIRES: Aliasing must be available for this data region (test with
0263 // upb_EpsCopyInputStream_AliasingAvailable().
0264 UPB_INLINE const char* upb_EpsCopyInputStream_ReadStringAliased(
0265     upb_EpsCopyInputStream* e, const char** ptr, size_t size) {
0266   UPB_ASSUME(upb_EpsCopyInputStream_AliasingAvailable(e, *ptr, size));
0267   const char* ret = *ptr + size;
0268   *ptr = upb_EpsCopyInputStream_GetAliasedPtr(e, *ptr);
0269   UPB_ASSUME(ret != NULL);
0270   return ret;
0271 }
0272 
0273 // Skips `size` bytes of data from the input and returns a pointer past the end.
0274 // Returns NULL on end of stream or error.
0275 UPB_INLINE const char* upb_EpsCopyInputStream_Skip(upb_EpsCopyInputStream* e,
0276                                                    const char* ptr, int size) {
0277   if (!upb_EpsCopyInputStream_CheckDataSizeAvailable(e, ptr, size)) return NULL;
0278   return ptr + size;
0279 }
0280 
0281 // Copies `size` bytes of data from the input `ptr` into the buffer `to`, and
0282 // returns a pointer past the end. Returns NULL on end of stream or error.
0283 UPB_INLINE const char* upb_EpsCopyInputStream_Copy(upb_EpsCopyInputStream* e,
0284                                                    const char* ptr, void* to,
0285                                                    int size) {
0286   if (!upb_EpsCopyInputStream_CheckDataSizeAvailable(e, ptr, size)) return NULL;
0287   memcpy(to, ptr, size);
0288   return ptr + size;
0289 }
0290 
0291 // Reads string data from the stream and advances the pointer accordingly.
0292 // If aliasing was enabled when the stream was initialized, then the returned
0293 // pointer will point into the input buffer if possible, otherwise new data
0294 // will be allocated from arena and copied into. We may be forced to copy even
0295 // if aliasing was enabled if the input data spans input buffers.
0296 //
0297 // Returns NULL if memory allocation failed, or we reached a premature EOF.
0298 UPB_INLINE const char* upb_EpsCopyInputStream_ReadString(
0299     upb_EpsCopyInputStream* e, const char** ptr, size_t size,
0300     upb_Arena* arena) {
0301   if (upb_EpsCopyInputStream_AliasingAvailable(e, *ptr, size)) {
0302     return upb_EpsCopyInputStream_ReadStringAliased(e, ptr, size);
0303   } else {
0304     // We need to allocate and copy.
0305     if (!upb_EpsCopyInputStream_CheckDataSizeAvailable(e, *ptr, size)) {
0306       return NULL;
0307     }
0308     UPB_ASSERT(arena);
0309     char* data = (char*)upb_Arena_Malloc(arena, size);
0310     if (!data) return NULL;
0311     const char* ret = upb_EpsCopyInputStream_Copy(e, *ptr, data, size);
0312     *ptr = data;
0313     return ret;
0314   }
0315 }
0316 
0317 UPB_INLINE void _upb_EpsCopyInputStream_CheckLimit(upb_EpsCopyInputStream* e) {
0318   UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
0319 }
0320 
0321 // Pushes a limit onto the stack of limits for the current stream.  The limit
0322 // will extend for `size` bytes beyond the position in `ptr`.  Future calls to
0323 // upb_EpsCopyInputStream_IsDone() will return `true` when the stream position
0324 // reaches this limit.
0325 //
0326 // Returns a delta that the caller must store and supply to PopLimit() below.
0327 UPB_INLINE int upb_EpsCopyInputStream_PushLimit(upb_EpsCopyInputStream* e,
0328                                                 const char* ptr, int size) {
0329   int limit = size + (int)(ptr - e->end);
0330   int delta = e->limit - limit;
0331   _upb_EpsCopyInputStream_CheckLimit(e);
0332   UPB_ASSERT(limit <= e->limit);
0333   e->limit = limit;
0334   e->limit_ptr = e->end + UPB_MIN(0, limit);
0335   _upb_EpsCopyInputStream_CheckLimit(e);
0336   return delta;
0337 }
0338 
0339 // Pops the last limit that was pushed on this stream.  This may only be called
0340 // once IsDone() returns true.  The user must pass the delta that was returned
0341 // from PushLimit().
0342 UPB_INLINE void upb_EpsCopyInputStream_PopLimit(upb_EpsCopyInputStream* e,
0343                                                 const char* ptr,
0344                                                 int saved_delta) {
0345   UPB_ASSERT(ptr - e->end == e->limit);
0346   _upb_EpsCopyInputStream_CheckLimit(e);
0347   e->limit += saved_delta;
0348   e->limit_ptr = e->end + UPB_MIN(0, e->limit);
0349   _upb_EpsCopyInputStream_CheckLimit(e);
0350 }
0351 
0352 UPB_INLINE const char* _upb_EpsCopyInputStream_IsDoneFallbackInline(
0353     upb_EpsCopyInputStream* e, const char* ptr, int overrun,
0354     upb_EpsCopyInputStream_BufferFlipCallback* callback) {
0355   if (overrun < e->limit) {
0356     // Need to copy remaining data into patch buffer.
0357     UPB_ASSERT(overrun < kUpb_EpsCopyInputStream_SlopBytes);
0358     const char* old_end = ptr;
0359     const char* new_start = &e->patch[0] + overrun;
0360     memset(e->patch + kUpb_EpsCopyInputStream_SlopBytes, 0,
0361            kUpb_EpsCopyInputStream_SlopBytes);
0362     memcpy(e->patch, e->end, kUpb_EpsCopyInputStream_SlopBytes);
0363     ptr = new_start;
0364     e->end = &e->patch[kUpb_EpsCopyInputStream_SlopBytes];
0365     e->limit -= kUpb_EpsCopyInputStream_SlopBytes;
0366     e->limit_ptr = e->end + e->limit;
0367     UPB_ASSERT(ptr < e->limit_ptr);
0368     e->input_delta = (uintptr_t)old_end - (uintptr_t)new_start;
0369     return callback(e, old_end, new_start);
0370   } else {
0371     UPB_ASSERT(overrun > e->limit);
0372     e->error = true;
0373     return callback(e, NULL, NULL);
0374   }
0375 }
0376 
0377 typedef const char* upb_EpsCopyInputStream_ParseDelimitedFunc(
0378     upb_EpsCopyInputStream* e, const char* ptr, void* ctx);
0379 
0380 // Tries to perform a fast-path handling of the given delimited message data.
0381 // If the sub-message beginning at `*ptr` and extending for `len` is short and
0382 // fits within this buffer, calls `func` with `ctx` as a parameter, where the
0383 // pushing and popping of limits is handled automatically and with lower cost
0384 // than the normal PushLimit()/PopLimit() sequence.
0385 UPB_FORCEINLINE bool upb_EpsCopyInputStream_TryParseDelimitedFast(
0386     upb_EpsCopyInputStream* e, const char** ptr, int len,
0387     upb_EpsCopyInputStream_ParseDelimitedFunc* func, void* ctx) {
0388   if (!upb_EpsCopyInputStream_CheckSubMessageSizeAvailable(e, *ptr, len)) {
0389     return false;
0390   }
0391 
0392   // Fast case: Sub-message is <128 bytes and fits in the current buffer.
0393   // This means we can preserve limit/limit_ptr verbatim.
0394   const char* saved_limit_ptr = e->limit_ptr;
0395   int saved_limit = e->limit;
0396   e->limit_ptr = *ptr + len;
0397   e->limit = e->limit_ptr - e->end;
0398   UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
0399   *ptr = func(e, *ptr, ctx);
0400   e->limit_ptr = saved_limit_ptr;
0401   e->limit = saved_limit;
0402   UPB_ASSERT(e->limit_ptr == e->end + UPB_MIN(0, e->limit));
0403   return true;
0404 }
0405 
0406 #ifdef __cplusplus
0407 } /* extern "C" */
0408 #endif
0409 
0410 #include "upb/port/undef.inc"
0411 
0412 #endif  // UPB_WIRE_EPS_COPY_INPUT_STREAM_H_