Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc / deps / grpc / third_party / upb / upb / upb.c
1
2 #include "upb/upb.h"
3
4 #include <errno.h>
5 #include <stdarg.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "upb/port_def.inc"
13
14 /* Guarantee null-termination and provide ellipsis truncation.
15  * It may be tempting to "optimize" this by initializing these final
16  * four bytes up-front and then being careful never to overwrite them,
17  * this is safer and simpler. */
18 static void nullz(upb_status *status) {
19   const char *ellipsis = "...";
20   size_t len = strlen(ellipsis);
21   UPB_ASSERT(sizeof(status->msg) > len);
22   memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len);
23 }
24
25 /* upb_status *****************************************************************/
26
27 void upb_status_clear(upb_status *status) {
28   if (!status) return;
29   status->ok = true;
30   status->msg[0] = '\0';
31 }
32
33 bool upb_ok(const upb_status *status) { return status->ok; }
34
35 const char *upb_status_errmsg(const upb_status *status) { return status->msg; }
36
37 void upb_status_seterrmsg(upb_status *status, const char *msg) {
38   if (!status) return;
39   status->ok = false;
40   strncpy(status->msg, msg, sizeof(status->msg));
41   nullz(status);
42 }
43
44 void upb_status_seterrf(upb_status *status, const char *fmt, ...) {
45   va_list args;
46   va_start(args, fmt);
47   upb_status_vseterrf(status, fmt, args);
48   va_end(args);
49 }
50
51 void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) {
52   if (!status) return;
53   status->ok = false;
54   _upb_vsnprintf(status->msg, sizeof(status->msg), fmt, args);
55   nullz(status);
56 }
57
58 /* upb_alloc ******************************************************************/
59
60 static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
61                                   size_t size) {
62   UPB_UNUSED(alloc);
63   UPB_UNUSED(oldsize);
64   if (size == 0) {
65     free(ptr);
66     return NULL;
67   } else {
68     return realloc(ptr, size);
69   }
70 }
71
72 upb_alloc upb_alloc_global = {&upb_global_allocfunc};
73
74 /* upb_arena ******************************************************************/
75
76 /* Be conservative and choose 16 in case anyone is using SSE. */
77 static const size_t maxalign = 16;
78
79 static size_t align_up_max(size_t size) {
80   return ((size + maxalign - 1) / maxalign) * maxalign;
81 }
82
83 struct upb_arena {
84   /* We implement the allocator interface.
85    * This must be the first member of upb_arena! */
86   upb_alloc alloc;
87
88   /* Allocator to allocate arena blocks.  We are responsible for freeing these
89    * when we are destroyed. */
90   upb_alloc *block_alloc;
91
92   size_t bytes_allocated;
93   size_t next_block_size;
94   size_t max_block_size;
95
96   /* Linked list of blocks.  Points to an arena_block, defined in env.c */
97   void *block_head;
98
99   /* Cleanup entries.  Pointer to a cleanup_ent, defined in env.c */
100   void *cleanup_head;
101 };
102
103 typedef struct mem_block {
104   struct mem_block *next;
105   size_t size;
106   size_t used;
107   bool owned;
108   /* Data follows. */
109 } mem_block;
110
111 typedef struct cleanup_ent {
112   struct cleanup_ent *next;
113   upb_cleanup_func *cleanup;
114   void *ud;
115 } cleanup_ent;
116
117 static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size,
118                                bool owned) {
119   mem_block *block = ptr;
120
121   block->next = a->block_head;
122   block->size = size;
123   block->used = align_up_max(sizeof(mem_block));
124   block->owned = owned;
125
126   a->block_head = block;
127
128   /* TODO(haberman): ASAN poison. */
129 }
130
131 static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) {
132   size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block);
133   mem_block *block = upb_malloc(a->block_alloc, block_size);
134
135   if (!block) {
136     return NULL;
137   }
138
139   upb_arena_addblock(a, block, block_size, true);
140   a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size);
141
142   return block;
143 }
144
145 static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize,
146                                size_t size) {
147   upb_arena *a = (upb_arena*)alloc;  /* upb_alloc is initial member. */
148   mem_block *block = a->block_head;
149   void *ret;
150
151   if (size == 0) {
152     return NULL;  /* We are an arena, don't need individual frees. */
153   }
154
155   size = align_up_max(size);
156
157   /* TODO(haberman): special-case if this is a realloc of the last alloc? */
158
159   if (!block || block->size - block->used < size) {
160     /* Slow path: have to allocate a new block. */
161     block = upb_arena_allocblock(a, size);
162
163     if (!block) {
164       return NULL;  /* Out of memory. */
165     }
166   }
167
168   ret = (char*)block + block->used;
169   block->used += size;
170
171   if (oldsize > 0) {
172     memcpy(ret, ptr, oldsize);  /* Preserve existing data. */
173   }
174
175   /* TODO(haberman): ASAN unpoison. */
176
177   a->bytes_allocated += size;
178   return ret;
179 }
180
181 /* Public Arena API ***********************************************************/
182
183 #define upb_alignof(type) offsetof (struct { char c; type member; }, member)
184
185 upb_arena *upb_arena_init(void *mem, size_t n, upb_alloc *alloc) {
186   const size_t first_block_overhead = sizeof(upb_arena) + sizeof(mem_block);
187   upb_arena *a;
188   bool owned = false;
189
190   /* Round block size down to alignof(*a) since we will allocate the arena
191    * itself at the end. */
192   n &= ~(upb_alignof(upb_arena) - 1);
193
194   if (n < first_block_overhead) {
195     /* We need to malloc the initial block. */
196     n = first_block_overhead + 256;
197     owned = true;
198     if (!alloc || !(mem = upb_malloc(alloc, n))) {
199       return NULL;
200     }
201   }
202
203   a = (void*)((char*)mem + n - sizeof(*a));
204   n -= sizeof(*a);
205
206   a->alloc.func = &upb_arena_doalloc;
207   a->block_alloc = &upb_alloc_global;
208   a->bytes_allocated = 0;
209   a->next_block_size = 256;
210   a->max_block_size = 16384;
211   a->cleanup_head = NULL;
212   a->block_head = NULL;
213   a->block_alloc = alloc;
214
215   upb_arena_addblock(a, mem, n, owned);
216
217   return a;
218 }
219
220 #undef upb_alignof
221
222 void upb_arena_free(upb_arena *a) {
223   cleanup_ent *ent = a->cleanup_head;
224   mem_block *block = a->block_head;
225
226   while (ent) {
227     ent->cleanup(ent->ud);
228     ent = ent->next;
229   }
230
231   /* Must do this after running cleanup functions, because this will delete
232    * the memory we store our cleanup entries in! */
233   while (block) {
234     /* Load first since we are deleting block. */
235     mem_block *next = block->next;
236
237     if (block->owned) {
238       upb_free(a->block_alloc, block);
239     }
240
241     block = next;
242   }
243 }
244
245 bool upb_arena_addcleanup(upb_arena *a, void *ud, upb_cleanup_func *func) {
246   cleanup_ent *ent = upb_malloc(&a->alloc, sizeof(cleanup_ent));
247   if (!ent) {
248     return false;  /* Out of memory. */
249   }
250
251   ent->cleanup = func;
252   ent->ud = ud;
253   ent->next = a->cleanup_head;
254   a->cleanup_head = ent;
255
256   return true;
257 }
258
259 size_t upb_arena_bytesallocated(const upb_arena *a) {
260   return a->bytes_allocated;
261 }