upb/upb/upb.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009-2021, Google LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of Google LLC nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <errno.h>
29 #include <float.h>
30 #include <stdarg.h>
31 #include <stddef.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "upb/upb_internal.h"
38 
39 // Must be last.
40 #include "upb/port_def.inc"
41 
42 /* upb_Status *****************************************************************/
43 
45  if (!status) return;
46  status->ok = true;
47  status->msg[0] = '\0';
48 }
49 
50 bool upb_Status_IsOk(const upb_Status* status) { return status->ok; }
51 
53  return status->msg;
54 }
55 
57  if (!status) return;
58  status->ok = false;
59  strncpy(status->msg, msg, _kUpb_Status_MaxMessage - 1);
60  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
61 }
62 
63 void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...) {
64  va_list args;
65  va_start(args, fmt);
67  va_end(args);
68 }
69 
71  va_list args) {
72  if (!status) return;
73  status->ok = false;
74  vsnprintf(status->msg, sizeof(status->msg), fmt, args);
75  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
76 }
77 
79  va_list args) {
80  size_t len;
81  if (!status) return;
82  status->ok = false;
83  len = strlen(status->msg);
84  vsnprintf(status->msg + len, sizeof(status->msg) - len, fmt, args);
85  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
86 }
87 
88 /* upb_alloc ******************************************************************/
89 
90 static void* upb_global_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize,
91  size_t size) {
93  UPB_UNUSED(oldsize);
94  if (size == 0) {
95  free(ptr);
96  return NULL;
97  } else {
98  return realloc(ptr, size);
99  }
100 }
101 
102 static uint32_t* upb_cleanup_pointer(uintptr_t cleanup_metadata) {
103  return (uint32_t*)(cleanup_metadata & ~0x1);
104 }
105 
106 static bool upb_cleanup_has_initial_block(uintptr_t cleanup_metadata) {
107  return cleanup_metadata & 0x1;
108 }
109 
111  bool has_initial_block) {
112  return (uintptr_t)cleanup | has_initial_block;
113 }
114 
116 
117 /* upb_Arena ******************************************************************/
118 
119 /* Be conservative and choose 16 in case anyone is using SSE. */
120 
121 struct mem_block {
122  struct mem_block* next;
123  uint32_t size;
125  /* Data follows. */
126 };
127 
128 typedef struct cleanup_ent {
130  void* ud;
131 } cleanup_ent;
132 
133 static const size_t memblock_reserve = UPB_ALIGN_UP(sizeof(mem_block), 16);
134 
136  /* Path splitting keeps time complexity down, see:
137  * https://en.wikipedia.org/wiki/Disjoint-set_data_structure */
138  while (a->parent != a) {
139  upb_Arena* next = a->parent;
140  a->parent = next->parent;
141  a = next;
142  }
143  return a;
144 }
145 
147  size_t size) {
148  mem_block* block = ptr;
149 
150  /* The block is for arena |a|, but should appear in the freelist of |root|. */
151  block->next = root->freelist;
152  block->size = (uint32_t)size;
153  block->cleanups = 0;
154  root->freelist = block;
155  a->last_size = block->size;
156  if (!root->freelist_tail) root->freelist_tail = block;
157 
158  a->head.ptr = UPB_PTR_AT(block, memblock_reserve, char);
159  a->head.end = UPB_PTR_AT(block, size, char);
160  a->cleanup_metadata = upb_cleanup_metadata(
161  &block->cleanups, upb_cleanup_has_initial_block(a->cleanup_metadata));
162 
163  UPB_POISON_MEMORY_REGION(a->head.ptr, a->head.end - a->head.ptr);
164 }
165 
166 static bool upb_Arena_Allocblock(upb_Arena* a, size_t size) {
168  size_t block_size = UPB_MAX(size, a->last_size * 2) + memblock_reserve;
169  mem_block* block = upb_malloc(root->block_alloc, block_size);
170 
171  if (!block) return false;
172  upb_Arena_addblock(a, root, block, block_size);
173  return true;
174 }
175 
177  if (!upb_Arena_Allocblock(a, size)) return NULL; /* Out of memory. */
179  return upb_Arena_Malloc(a, size);
180 }
181 
182 static void* upb_Arena_doalloc(upb_alloc* alloc, void* ptr, size_t oldsize,
183  size_t size) {
184  upb_Arena* a = (upb_Arena*)alloc; /* upb_alloc is initial member. */
185  return upb_Arena_Realloc(a, ptr, oldsize, size);
186 }
187 
188 /* Public Arena API ***********************************************************/
189 
191  const size_t first_block_overhead = sizeof(upb_Arena) + memblock_reserve;
192  upb_Arena* a;
193 
194  /* We need to malloc the initial block. */
195  n = first_block_overhead + 256;
196  if (!alloc || !(mem = upb_malloc(alloc, n))) {
197  return NULL;
198  }
199 
200  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena);
201  n -= sizeof(*a);
202 
203  a->head.alloc.func = &upb_Arena_doalloc;
204  a->block_alloc = alloc;
205  a->parent = a;
206  a->refcount = 1;
207  a->freelist = NULL;
208  a->freelist_tail = NULL;
209  a->cleanup_metadata = upb_cleanup_metadata(NULL, false);
210 
211  upb_Arena_addblock(a, a, mem, n);
212 
213  return a;
214 }
215 
217  upb_Arena* a;
218 
219  if (n) {
220  /* Align initial pointer up so that we return properly-aligned pointers. */
221  void* aligned = (void*)UPB_ALIGN_UP((uintptr_t)mem, 16);
222  size_t delta = (uintptr_t)aligned - (uintptr_t)mem;
223  n = delta <= n ? n - delta : 0;
224  mem = aligned;
225  }
226 
227  /* Round block size down to alignof(*a) since we will allocate the arena
228  * itself at the end. */
230 
231  if (UPB_UNLIKELY(n < sizeof(upb_Arena))) {
232  return arena_initslow(mem, n, alloc);
233  }
234 
235  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena);
236 
237  a->head.alloc.func = &upb_Arena_doalloc;
238  a->block_alloc = alloc;
239  a->parent = a;
240  a->refcount = 1;
241  a->last_size = UPB_MAX(128, n);
242  a->head.ptr = mem;
243  a->head.end = UPB_PTR_AT(mem, n - sizeof(*a), char);
244  a->freelist = NULL;
245  a->cleanup_metadata = upb_cleanup_metadata(NULL, true);
246 
247  return a;
248 }
249 
250 static void arena_dofree(upb_Arena* a) {
251  mem_block* block = a->freelist;
252  UPB_ASSERT(a->parent == a);
253  UPB_ASSERT(a->refcount == 0);
254 
255  while (block) {
256  /* Load first since we are deleting block. */
257  mem_block* next = block->next;
258 
259  if (block->cleanups > 0) {
260  cleanup_ent* end = UPB_PTR_AT(block, block->size, void);
261  cleanup_ent* ptr = end - block->cleanups;
262 
263  for (; ptr < end; ptr++) {
264  ptr->cleanup(ptr->ud);
265  }
266  }
267 
268  upb_free(a->block_alloc, block);
269  block = next;
270  }
271 }
272 
274  a = arena_findroot(a);
275  if (--a->refcount == 0) arena_dofree(a);
276 }
277 
279  cleanup_ent* ent;
280  uint32_t* cleanups = upb_cleanup_pointer(a->cleanup_metadata);
281 
282  if (!cleanups || _upb_ArenaHas(a) < sizeof(cleanup_ent)) {
283  if (!upb_Arena_Allocblock(a, 128)) return false; /* Out of memory. */
284  UPB_ASSERT(_upb_ArenaHas(a) >= sizeof(cleanup_ent));
285  cleanups = upb_cleanup_pointer(a->cleanup_metadata);
286  }
287 
288  a->head.end -= sizeof(cleanup_ent);
289  ent = (cleanup_ent*)a->head.end;
290  (*cleanups)++;
292 
293  ent->cleanup = func;
294  ent->ud = ud;
295 
296  return true;
297 }
298 
300  upb_Arena* r1 = arena_findroot(a1);
301  upb_Arena* r2 = arena_findroot(a2);
302 
303  if (r1 == r2) return true; /* Already fused. */
304 
305  /* Do not fuse initial blocks since we cannot lifetime extend them. */
306  if (upb_cleanup_has_initial_block(r1->cleanup_metadata)) return false;
307  if (upb_cleanup_has_initial_block(r2->cleanup_metadata)) return false;
308 
309  /* Only allow fuse with a common allocator */
310  if (r1->block_alloc != r2->block_alloc) return false;
311 
312  /* We want to join the smaller tree to the larger tree.
313  * So swap first if they are backwards. */
314  if (r1->refcount < r2->refcount) {
315  upb_Arena* tmp = r1;
316  r1 = r2;
317  r2 = tmp;
318  }
319 
320  /* r1 takes over r2's freelist and refcount. */
321  r1->refcount += r2->refcount;
322  if (r2->freelist_tail) {
323  UPB_ASSERT(r2->freelist_tail->next == NULL);
324  r2->freelist_tail->next = r1->freelist;
325  r1->freelist = r2->freelist;
326  }
327  r2->parent = r1;
328  return true;
329 }
330 
331 /* Miscellaneous utilities ****************************************************/
332 
333 static void upb_FixLocale(char* p) {
334  /* printf() is dependent on locales; sadly there is no easy and portable way
335  * to avoid this. This little post-processing step will translate 1,2 -> 1.2
336  * since JSON needs the latter. Arguably a hack, but it is simple and the
337  * alternatives are far more complicated, platform-dependent, and/or larger
338  * in code size. */
339  for (; *p; p++) {
340  if (*p == ',') *p = '.';
341  }
342 }
343 
344 void _upb_EncodeRoundTripDouble(double val, char* buf, size_t size) {
345  assert(size >= kUpb_RoundTripBufferSize);
346  snprintf(buf, size, "%.*g", DBL_DIG, val);
347  if (strtod(buf, NULL) != val) {
348  snprintf(buf, size, "%.*g", DBL_DIG + 2, val);
349  assert(strtod(buf, NULL) == val);
350  }
352 }
353 
354 void _upb_EncodeRoundTripFloat(float val, char* buf, size_t size) {
355  assert(size >= kUpb_RoundTripBufferSize);
356  snprintf(buf, size, "%.*g", FLT_DIG, val);
357  if (strtof(buf, NULL) != val) {
358  snprintf(buf, size, "%.*g", FLT_DIG + 3, val);
359  assert(strtof(buf, NULL) == val);
360  }
362 }
ptr
char * ptr
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:45
upb_internal.h
mem_block::cleanups
uint32_t cleanups
Definition: php-upb.c:2752
vsnprintf
int __cdecl vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
Definition: libc.cpp:135
upb_Arena_Realloc
UPB_INLINE void * upb_Arena_Realloc(upb_Arena *a, void *ptr, size_t oldsize, size_t size)
Definition: upb/upb/upb.h:246
_upb_ArenaHas
UPB_INLINE size_t _upb_ArenaHas(upb_Arena *a)
Definition: upb/upb/upb.h:194
UPB_UNLIKELY
#define UPB_UNLIKELY(x)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:62
upb_CleanupFunc
void upb_CleanupFunc(void *ud)
Definition: upb/upb/upb.h:169
string.h
upb_Arena_Init
upb_Arena * upb_Arena_Init(void *mem, size_t n, upb_alloc *alloc)
Definition: upb/upb/upb.c:216
cleanup_ent
struct cleanup_ent cleanup_ent
buf
voidpf void * buf
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
UPB_ALIGN_UP
#define UPB_ALIGN_UP(size, align)
Definition: php-upb.c:93
upb_FixLocale
static void upb_FixLocale(char *p)
Definition: upb/upb/upb.c:333
status
absl::Status status
Definition: rls.cc:251
upb_cleanup_has_initial_block
static bool upb_cleanup_has_initial_block(uintptr_t cleanup_metadata)
Definition: upb/upb/upb.c:106
a
int a
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:88
arena_dofree
static void arena_dofree(upb_Arena *a)
Definition: upb/upb/upb.c:250
xds_manager.p
p
Definition: xds_manager.py:60
upb_cleanup_metadata
static uintptr_t upb_cleanup_metadata(uint32_t *cleanup, bool has_initial_block)
Definition: upb/upb/upb.c:110
UPB_PTR_AT
#define UPB_PTR_AT(msg, ofs, type)
Definition: php-upb.c:71
upb_Status_ErrorMessage
const char * upb_Status_ErrorMessage(const upb_Status *status)
Definition: upb/upb/upb.c:52
upb_Status_Clear
void upb_Status_Clear(upb_Status *status)
Definition: upb/upb/upb.c:44
_upb_Arena_SlowMalloc
void * _upb_Arena_SlowMalloc(upb_Arena *a, size_t size)
Definition: upb/upb/upb.c:176
block
Block * block
Definition: protobuf/src/google/protobuf/descriptor.cc:1041
uint32_t
unsigned int uint32_t
Definition: stdint-msvc2008.h:80
upb_Arena::freelist
mem_block * freelist
Definition: upb_internal.h:55
cleanup_ent::ud
void * ud
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:2283
upb_Status_SetErrorMessage
void upb_Status_SetErrorMessage(upb_Status *status, const char *msg)
Definition: upb/upb/upb.c:56
memblock_reserve
static const size_t memblock_reserve
Definition: upb/upb/upb.c:133
upb_Status_VSetErrorFormat
void upb_Status_VSetErrorFormat(upb_Status *status, const char *fmt, va_list args)
Definition: upb/upb/upb.c:70
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
a2
T::first_type a2
Definition: abseil-cpp/absl/container/internal/hash_function_defaults_test.cc:307
end
char * end
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1008
_kUpb_Status_MaxMessage
#define _kUpb_Status_MaxMessage
Definition: upb/upb/upb.h:50
cleanup_ent::cleanup
upb_CleanupFunc * cleanup
Definition: upb/upb/upb.c:129
root
RefCountedPtr< grpc_tls_certificate_provider > root
Definition: xds_server_config_fetcher.cc:223
upb_global_allocfunc
static void * upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize, size_t size)
Definition: upb/upb/upb.c:90
upb_Arena::parent
struct upb_Arena * parent
Definition: upb_internal.h:52
upb_Arena_Malloc
UPB_INLINE void * upb_Arena_Malloc(upb_Arena *a, size_t size)
Definition: upb/upb/upb.h:222
upb_Status_VAppendErrorFormat
void upb_Status_VAppendErrorFormat(upb_Status *status, const char *fmt, va_list args)
Definition: upb/upb/upb.c:78
arena_initslow
upb_Arena * arena_initslow(void *mem, size_t n, upb_alloc *alloc)
Definition: upb/upb/upb.c:190
cleanup_ent
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:2280
UPB_ALIGN_OF
#define UPB_ALIGN_OF(type)
Definition: php-upb.c:96
upb_Status_SetErrorFormat
void upb_Status_SetErrorFormat(upb_Status *status, const char *fmt,...)
Definition: upb/upb/upb.c:63
upb_Arena_AddCleanup
bool upb_Arena_AddCleanup(upb_Arena *a, void *ud, upb_CleanupFunc *func)
Definition: upb/upb/upb.c:278
upb_Arena_addblock
static void upb_Arena_addblock(upb_Arena *a, upb_Arena *root, void *ptr, size_t size)
Definition: upb/upb/upb.c:146
upb_Status
Definition: upb/upb/upb.h:52
UPB_ASSERT
#define UPB_ASSERT(expr)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:135
mem_block
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:2272
uintptr_t
_W64 unsigned int uintptr_t
Definition: stdint-msvc2008.h:119
a1
T::first_type a1
Definition: abseil-cpp/absl/container/internal/hash_function_defaults_test.cc:305
mem_block::next
struct mem_block * next
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:2273
upb_malloc
UPB_INLINE void * upb_malloc(upb_alloc *alloc, size_t size)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.h:303
UPB_ALIGN_DOWN
#define UPB_ALIGN_DOWN(size, align)
Definition: php-upb.c:94
upb_Arena::cleanup_metadata
uintptr_t cleanup_metadata
Definition: upb_internal.h:41
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
stdint.h
msg
std::string msg
Definition: client_interceptors_end2end_test.cc:372
mem
void * mem
Definition: libc.cpp:91
upb_free
UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.h:314
UPB_MAX
#define UPB_MAX(x, y)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:125
testing::internal::fmt
GTEST_API_ const char * fmt
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1808
mem_block::size
size_t size
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:2274
func
const EVP_CIPHER *(* func)(void)
Definition: cipher_extra.c:73
UPB_UNUSED
#define UPB_UNUSED(var)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:128
upb_Status_IsOk
bool upb_Status_IsOk(const upb_Status *status)
Definition: upb/upb/upb.c:50
upb_Arena_doalloc
static void * upb_Arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize, size_t size)
Definition: upb/upb/upb.c:182
next
AllocList * next[kMaxLevel]
Definition: abseil-cpp/absl/base/internal/low_level_alloc.cc:100
upb_Arena::block_alloc
upb_alloc * block_alloc
Definition: upb_internal.h:45
upb_Arena::refcount
uint32_t refcount
Definition: upb_internal.h:51
upb_Arena
struct upb_Arena upb_Arena
Definition: upb/upb/upb.h:172
absl::Status::ok
ABSL_MUST_USE_RESULT bool ok() const
Definition: third_party/abseil-cpp/absl/status/status.h:802
_upb_EncodeRoundTripFloat
void _upb_EncodeRoundTripFloat(float val, char *buf, size_t size)
Definition: upb/upb/upb.c:354
cleanup_ent::cleanup
upb_cleanup_func * cleanup
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:2282
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
autogen_x86imm.tmp
tmp
Definition: autogen_x86imm.py:12
cleanup
Definition: cleanup.py:1
kUpb_RoundTripBufferSize
@ kUpb_RoundTripBufferSize
Definition: upb_internal.h:64
size
voidpf void uLong size
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
upb_alloc
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.h:299
upb_Arena_Allocblock
static bool upb_Arena_Allocblock(upb_Arena *a, size_t size)
Definition: upb/upb/upb.c:166
upb_Arena
Definition: upb_internal.h:36
_upb_EncodeRoundTripDouble
void _upb_EncodeRoundTripDouble(double val, char *buf, size_t size)
Definition: upb/upb/upb.c:344
upb_Arena_Fuse
bool upb_Arena_Fuse(upb_Arena *a1, upb_Arena *a2)
Definition: upb/upb/upb.c:299
upb_Arena_Free
void upb_Arena_Free(upb_Arena *a)
Definition: upb/upb/upb.c:273
upb_cleanup_pointer
static uint32_t * upb_cleanup_pointer(uintptr_t cleanup_metadata)
Definition: upb/upb/upb.c:102
arena_findroot
static upb_Arena * arena_findroot(upb_Arena *a)
Definition: upb/upb/upb.c:135
alloc
std::allocator< int > alloc
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:87
UPB_UNPOISON_MEMORY_REGION
#define UPB_UNPOISON_MEMORY_REGION(addr, size)
Definition: php-upb.c:253
upb_alloc_global
upb_alloc upb_alloc_global
Definition: upb/upb/upb.c:115
errno.h
UPB_POISON_MEMORY_REGION
#define UPB_POISON_MEMORY_REGION(addr, size)
Definition: php-upb.c:251
upb_Arena::freelist_tail
mem_block * freelist_tail
Definition: upb_internal.h:55


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:47