From 3bd691a4975b2267ff04611507e766a7f9f87e83 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Fri, 8 May 2015 16:56:29 -0700 Subject: Google-internal development. --- upb/env.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 upb/env.c (limited to 'upb/env.c') diff --git a/upb/env.c b/upb/env.c new file mode 100644 index 0000000..ae5fb85 --- /dev/null +++ b/upb/env.c @@ -0,0 +1,259 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2014 Google Inc. See LICENSE for details. + * Author: Josh Haberman + */ + +#include "upb/env.h" + +#include +#include +#include + +typedef struct cleanup_ent { + upb_cleanup_func *cleanup; + void *ud; + struct cleanup_ent *next; +} cleanup_ent; + +static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size); + +/* Default allocator **********************************************************/ + +// Just use realloc, keeping all allocated blocks in a linked list to destroy at +// the end. + +typedef struct mem_block { + // List is doubly-linked, because in cases where realloc() moves an existing + // block, we need to be able to remove the old pointer from the list + // efficiently. + struct mem_block *prev, *next; +#ifndef NDEBUG + size_t size; // Doesn't include mem_block structure. +#endif + char data[]; +} mem_block; + +typedef struct { + mem_block *head; +} default_alloc_ud; + +static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { + UPB_UNUSED(oldsize); + default_alloc_ud *ud = _ud; + + mem_block *from = ptr ? ptr - sizeof(mem_block) : NULL; + +#ifndef NDEBUG + if (from) { + assert(oldsize <= from->size); + } +#endif + + mem_block *block = realloc(from, size + sizeof(mem_block)); + if (!block) return NULL; + +#ifndef NDEBUG + block->size = size; +#endif + + if (from) { + if (block != from) { + // The block was moved, so pointers in next and prev blocks must be + // updated to its new location. + if (block->next) block->next->prev = block; + if (block->prev) block->prev->next = block; + } + } else { + // Insert at head of linked list. + block->prev = NULL; + block->next = ud->head; + if (block->next) block->next->prev = block; + ud->head = block; + } + + return &block->data; +} + +static void default_alloc_cleanup(void *_ud) { + default_alloc_ud *ud = _ud; + mem_block *block = ud->head; + + while (block) { + void *to_free = block; + block = block->next; + free(to_free); + } +} + + +/* Standard error functions ***************************************************/ + +static bool default_err(void *ud, const upb_status *status) { + UPB_UNUSED(ud); + fprintf(stderr, "upb error: %s\n", upb_status_errmsg(status)); + return false; +} + +static bool write_err_to(void *ud, const upb_status *status) { + upb_status *copy_to = ud; + upb_status_copy(copy_to, status); + return false; +} + + +/* upb_env ********************************************************************/ + +void upb_env_init(upb_env *e) { + e->ok_ = true; + e->bytes_allocated = 0; + e->cleanup_head = NULL; + + default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; + ud->head = NULL; + + // Set default functions. + upb_env_setallocfunc(e, default_alloc, ud); + upb_env_seterrorfunc(e, default_err, NULL); +} + +void upb_env_uninit(upb_env *e) { + cleanup_ent *ent = e->cleanup_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! + if (e->alloc == default_alloc) { + default_alloc_cleanup(e->alloc_ud); + } +} + +UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc, + void *ud) { + e->alloc = alloc; + e->alloc_ud = ud; +} + +UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, + void *ud) { + e->err = func; + e->err_ud = ud; +} + +void upb_env_reporterrorsto(upb_env *e, upb_status *status) { + e->err = write_err_to; + e->err_ud = status; +} + +bool upb_env_ok(const upb_env *e) { + return e->ok_; +} + +bool upb_env_reporterror(upb_env *e, const upb_status *status) { + e->ok_ = false; + return e->err(e->err_ud, status); +} + +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { + cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent)); + if (!ent) return false; + + ent->cleanup = func; + ent->ud = ud; + ent->next = e->cleanup_head; + e->cleanup_head = ent; + + return true; +} + +void *upb_env_malloc(upb_env *e, size_t size) { + e->bytes_allocated += size; + if (e->alloc == seeded_alloc) { + // This is equivalent to the next branch, but allows inlining for a + // measurable perf benefit. + return seeded_alloc(e->alloc_ud, NULL, 0, size); + } else { + return e->alloc(e->alloc_ud, NULL, 0, size); + } +} + +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { + assert(oldsize <= size); + char *ret = e->alloc(e->alloc_ud, ptr, oldsize, size); + +#ifndef NDEBUG + // Overwrite non-preserved memory to ensure callers are passing the oldsize + // that they truly require. + memset(ret + oldsize, 0xff, size - oldsize); +#endif + + return ret; +} + +size_t upb_env_bytesallocated(const upb_env *e) { + return e->bytes_allocated; +} + + +/* upb_seededalloc ************************************************************/ + +// Be conservative and choose 16 in case anyone is using SSE. +static const size_t maxalign = 16; + +static size_t align_up(size_t size) { + return ((size + maxalign - 1) / maxalign) * maxalign; +} + +UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, + size_t size) { + upb_seededalloc *a = ud; + size = align_up(size); + + if (oldsize == 0 && size <= a->mem_limit - a->mem_ptr) { + // Fast path: we can satisfy from the initial allocation. + void *ret = a->mem_ptr; + a->mem_ptr += size; + return ret; + } else { + // Slow path: fallback to other allocator. + a->need_cleanup = true; + return a->alloc(a->alloc_ud, ptr, oldsize, size); + } +} + +void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) { + a->mem_base = mem; + a->mem_ptr = mem; + a->mem_limit = mem + len; + a->need_cleanup = false; + a->returned_allocfunc = false; + + default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; + ud->head = NULL; + + upb_seededalloc_setfallbackalloc(a, default_alloc, ud); +} + +void upb_seededalloc_uninit(upb_seededalloc *a) { + if (a->alloc == default_alloc && a->need_cleanup) { + default_alloc_cleanup(a->alloc_ud); + } +} + +UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, + upb_alloc_func *alloc, + void *ud) { + assert(!a->returned_allocfunc); + a->alloc = alloc; + a->alloc_ud = ud; +} + +upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { + a->returned_allocfunc = true; + return seeded_alloc; +} -- cgit v1.2.3