#include "upb/upb.h" #include #include #include #include #include #include #include #include "upb/port_def.inc" /* upb_status *****************************************************************/ void upb_status_clear(upb_status *status) { if (!status) return; status->ok = true; status->msg[0] = '\0'; } bool upb_ok(const upb_status *status) { return status->ok; } const char *upb_status_errmsg(const upb_status *status) { return status->msg; } void upb_status_seterrmsg(upb_status *status, const char *msg) { if (!status) return; status->ok = false; strncpy(status->msg, msg, UPB_STATUS_MAX_MESSAGE - 1); status->msg[UPB_STATUS_MAX_MESSAGE - 1] = '\0'; } void upb_status_seterrf(upb_status *status, const char *fmt, ...) { va_list args; va_start(args, fmt); upb_status_vseterrf(status, fmt, args); va_end(args); } void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) { if (!status) return; status->ok = false; _upb_vsnprintf(status->msg, sizeof(status->msg), fmt, args); status->msg[UPB_STATUS_MAX_MESSAGE - 1] = '\0'; } /* upb_alloc ******************************************************************/ static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize, size_t size) { UPB_UNUSED(alloc); UPB_UNUSED(oldsize); if (size == 0) { free(ptr); return NULL; } else { return realloc(ptr, size); } } upb_alloc upb_alloc_global = {&upb_global_allocfunc}; /* upb_arena ******************************************************************/ /* Be conservative and choose 16 in case anyone is using SSE. */ struct upb_arena { _upb_arena_head head; char *start; /* Allocator to allocate arena blocks. We are responsible for freeing these * when we are destroyed. */ upb_alloc *block_alloc; size_t bytes_allocated; size_t next_block_size; size_t max_block_size; /* Linked list of blocks. Points to an arena_block, defined in env.c */ void *block_head; /* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */ void *cleanup_head; }; typedef struct mem_block { struct mem_block *next; bool owned; /* Data follows. */ } mem_block; typedef struct cleanup_ent { struct cleanup_ent *next; upb_cleanup_func *cleanup; void *ud; } cleanup_ent; static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size, bool owned) { mem_block *block = ptr; if (a->block_head) { a->bytes_allocated += a->head.ptr - a->start; } block->next = a->block_head; block->owned = owned; a->block_head = block; a->start = (char*)block + _upb_arena_alignup(sizeof(mem_block)); a->head.ptr = a->start; a->head.end = (char*)block + size; /* TODO(haberman): ASAN poison. */ } static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) { size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block); mem_block *block = upb_malloc(a->block_alloc, block_size); if (!block) { return NULL; } upb_arena_addblock(a, block, block_size, true); a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size); return block; } void *_upb_arena_slowmalloc(upb_arena *a, size_t size) { mem_block *block = upb_arena_allocblock(a, size); if (!block) return NULL; /* Out of memory. */ return upb_arena_malloc(a, size); } static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize, size_t size) { upb_arena *a = (upb_arena*)alloc; /* upb_alloc is initial member. */ void *ret; if (size == 0) { return NULL; /* We are an arena, don't need individual frees. */ } ret = upb_arena_malloc(a, size); if (!ret) return NULL; /* TODO(haberman): special-case if this is a realloc of the last alloc? */ if (oldsize > 0) { memcpy(ret, ptr, oldsize); /* Preserve existing data. */ } /* TODO(haberman): ASAN unpoison. */ return ret; } /* Public Arena API ***********************************************************/ #define upb_alignof(type) offsetof (struct { char c; type member; }, member) upb_arena *upb_arena_init(void *mem, size_t n, upb_alloc *alloc) { const size_t first_block_overhead = sizeof(upb_arena) + sizeof(mem_block); upb_arena *a; bool owned = false; /* Round block size down to alignof(*a) since we will allocate the arena * itself at the end. */ n &= ~(upb_alignof(upb_arena) - 1); if (n < first_block_overhead) { /* We need to malloc the initial block. */ n = first_block_overhead + 256; owned = true; if (!alloc || !(mem = upb_malloc(alloc, n))) { return NULL; } } a = (void*)((char*)mem + n - sizeof(*a)); n -= sizeof(*a); a->head.alloc.func = &upb_arena_doalloc; a->head.ptr = NULL; a->head.end = NULL; a->start = NULL; a->block_alloc = &upb_alloc_global; a->bytes_allocated = 0; a->next_block_size = 256; a->max_block_size = 16384; a->cleanup_head = NULL; a->block_head = NULL; a->block_alloc = alloc; upb_arena_addblock(a, mem, n, owned); return a; } #undef upb_alignof void upb_arena_free(upb_arena *a) { cleanup_ent *ent = a->cleanup_head; mem_block *block = a->block_head; while (ent) { ent->cleanup(ent->ud); ent = ent->next; } /* Must do this after running cleanup functions, because this will delete * the memory we store our cleanup entries in! */ while (block) { /* Load first since we are deleting block. */ mem_block *next = block->next; if (block->owned) { upb_free(a->block_alloc, block); } block = next; } } bool upb_arena_addcleanup(upb_arena *a, void *ud, upb_cleanup_func *func) { cleanup_ent *ent = upb_malloc(&a->head.alloc, sizeof(cleanup_ent)); if (!ent) { return false; /* Out of memory. */ } ent->cleanup = func; ent->ud = ud; ent->next = a->cleanup_head; a->cleanup_head = ent; return true; } size_t upb_arena_bytesallocated(const upb_arena *a) { return a->bytes_allocated + (a->head.ptr - a->start); }