|
| 1 | +// Protocol Buffers - Google's data interchange format |
| 2 | +// Copyright 2025 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 GOOGLE_UPB_UPB_BASE_ERROR_HANDLER_H__ |
| 9 | +#define GOOGLE_UPB_UPB_BASE_ERROR_HANDLER_H__ |
| 10 | + |
| 11 | +#include <setjmp.h> |
| 12 | + |
| 13 | +// Must be last. |
| 14 | +#include "upb/port/def.inc" |
| 15 | + |
| 16 | +// upb_ErrorHandler is a standard longjmp()-based exception handler for UPB. |
| 17 | +// It is used for efficient error handling in cases where longjmp() is safe to |
| 18 | +// use, such as in highly performance-sensitive C parsing code. |
| 19 | +// |
| 20 | +// This structure contains both a jmp_buf and an error code; the error code is |
| 21 | +// stored in the structure prior to calling longjmp(). This is necessary because |
| 22 | +// per the C standard, it is not possible to store the result of setjmp(), so |
| 23 | +// the error code must be passed out-of-band. |
| 24 | +// |
| 25 | +// upb_ErrorHandler is generally not C++-compatible, because longjmp() does not |
| 26 | +// run C++ destructors. So any library that supports upb_ErrorHandler should |
| 27 | +// also support a regular return-based error handling mechanism. (Note: we |
| 28 | +// could conceivably extend this to take a callback, which could either call |
| 29 | +// longjmp() or throw a C++ exception. But since C++ exceptions are forbidden |
| 30 | +// by the C++ style guide, there's not likely to be a demand for this.) |
| 31 | +// |
| 32 | +// To support both cases (longjmp() or return-based status) efficiently, code |
| 33 | +// can be written like this: |
| 34 | +// |
| 35 | +// UPB_ATTR_CONST bool upb_Arena_HasErrHandler(const upb_Arena* a); |
| 36 | +// |
| 37 | +// INLINE void* upb_Arena_Malloc(upb_Arena* a, size_t size) { |
| 38 | +// if (UPB_UNLIKELY(a->end - a->ptr < size)) { |
| 39 | +// void* ret = upb_Arena_MallocFallback(a, size); |
| 40 | +// UPB_MAYBE_ASSUME(upb_Arena_HasErrHandler(a), ret != NULL); |
| 41 | +// return ret; |
| 42 | +// } |
| 43 | +// void* ret = a->ptr; |
| 44 | +// a->ptr += size; |
| 45 | +// UPB_ASSUME(ret != NULL); |
| 46 | +// return ret; |
| 47 | +// } |
| 48 | +// |
| 49 | +// If the optimizer can prove that an error handler is present, it can assume |
| 50 | +// that upb_Arena_Malloc() will not return NULL. |
| 51 | + |
| 52 | +// We need to standardize on any error code that might be thrown by an error |
| 53 | +// handler. |
| 54 | + |
| 55 | +typedef enum { |
| 56 | + kUpb_ErrorCode_Ok = 0, |
| 57 | + kUpb_ErrorCode_OutOfMemory = 1, |
| 58 | + kUpb_ErrorCode_Malformed = 2, |
| 59 | +} upb_ErrorCode; |
| 60 | + |
| 61 | +typedef struct { |
| 62 | + int code; |
| 63 | + jmp_buf buf; |
| 64 | +} upb_ErrorHandler; |
| 65 | + |
| 66 | +UPB_INLINE void upb_ErrorHandler_Init(upb_ErrorHandler* e) { |
| 67 | + e->code = kUpb_ErrorCode_Ok; |
| 68 | +} |
| 69 | + |
| 70 | +UPB_INLINE UPB_NORETURN void upb_ErrorHandler_ThrowError(upb_ErrorHandler* e, |
| 71 | + int code) { |
| 72 | + UPB_ASSERT(code != kUpb_ErrorCode_Ok); |
| 73 | + e->code = code; |
| 74 | + UPB_LONGJMP(e->buf, 1); |
| 75 | +} |
| 76 | + |
| 77 | +#include "upb/port/undef.inc" |
| 78 | + |
| 79 | +#endif // GOOGLE_UPB_UPB_BASE_ERROR_HANDLER_H__ |
0 commit comments