From e4635f223e7d36dfbea3b722a4ca4807a7e882e2 Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Tue, 21 Jun 2022 10:43:08 -0700 Subject: [PATCH] match file names to type names Lots of changes but it's all just moving things around. Backward-compatible stub #include's have been provided for now. upb_Arena/upb_Status have been split out from upb/upb.? upb_Array/upb_Map/upb_MessageValue have been split out from upb/collections.? upb_ExtensionRegistry has been split out from upb/msg.? upb/decode_internal.h is now upb/internal/decode.h upb/mini_table_accessors_internal.h is now upb/internal/mini_table_accessors.h upb/table_internal.h is now upb/internal/table.h upb/upb_internal.h is now upb/internal/upb.h PiperOrigin-RevId: 456297617 --- BUILD | 68 ++++- python/map.c | 2 +- python/protobuf.h | 1 - upb/arena.c | 277 ++++++++++++++++++++ upb/arena.h | 225 ++++++++++++++++ upb/array.c | 114 ++++++++ upb/array.h | 83 ++++++ upb/collections.h | 146 +---------- upb/decode.c | 4 +- upb/decode.h | 1 + upb/decode_fast.c | 2 +- upb/decode_internal.h | 181 +------------ upb/def.h | 2 +- upb/encode.c | 1 + upb/extension_registry.c | 93 +++++++ upb/extension_registry.h | 84 ++++++ upb/internal/decode.h | 211 +++++++++++++++ upb/internal/mini_table_accessors.h | 59 +++++ upb/internal/table.h | 385 ++++++++++++++++++++++++++++ upb/internal/upb.h | 68 +++++ upb/json_encode.c | 2 +- upb/{collections.c => map.c} | 88 +------ upb/map.h | 117 +++++++++ upb/message_value.h | 66 +++++ upb/mini_table_accessors.h | 4 +- upb/mini_table_accessors_internal.h | 29 +-- upb/mini_table_accessors_test.cc | 2 +- upb/msg.c | 62 +---- upb/msg.h | 47 +--- upb/msg_internal.h | 5 +- upb/reflection.c | 2 +- upb/reflection.h | 3 +- upb/status.c | 86 +++++++ upb/status.h | 66 +++++ upb/table.c | 4 +- upb/table_internal.h | 355 +------------------------ upb/test_table.cc | 2 +- upb/text_encode.c | 2 +- upb/upb.c | 293 +-------------------- upb/upb.h | 204 +-------------- upb/upb_internal.h | 38 +-- 41 files changed, 2042 insertions(+), 1442 deletions(-) create mode 100644 upb/arena.c create mode 100644 upb/arena.h create mode 100644 upb/array.c create mode 100644 upb/array.h create mode 100644 upb/extension_registry.c create mode 100644 upb/extension_registry.h create mode 100644 upb/internal/decode.h create mode 100644 upb/internal/mini_table_accessors.h create mode 100644 upb/internal/table.h create mode 100644 upb/internal/upb.h rename upb/{collections.c => map.c} (60%) create mode 100644 upb/map.h create mode 100644 upb/message_value.h create mode 100644 upb/status.c create mode 100644 upb/status.h diff --git a/BUILD b/BUILD index 29a996205d..95701d1d2f 100644 --- a/BUILD +++ b/BUILD @@ -102,32 +102,57 @@ cc_library( cc_library( name = "upb", srcs = [ + "upb/arena.c", "upb/decode.c", - "upb/decode_internal.h", "upb/encode.c", + "upb/internal/decode.h", + "upb/internal/table.h", + "upb/internal/upb.h", "upb/msg.c", "upb/msg_internal.h", + "upb/status.c", "upb/table.c", - "upb/table_internal.h", "upb/upb.c", - "upb/upb_internal.h", ], hdrs = [ + "upb/arena.h", "upb/decode.h", "upb/encode.h", + "upb/extension_registry.h", "upb/msg.h", + "upb/status.h", "upb/upb.h", "upb/upb.hpp", ], copts = UPB_DEFAULT_COPTS, visibility = ["//visibility:public"], deps = [ + ":extension_registry", ":fastdecode", ":port", "//third_party/utf8_range", ], ) +cc_library( + name = "extension_registry", + srcs = [ + "upb/extension_registry.c", + "upb/msg.h", + "upb/msg_internal.h", + "upb/upb.h", + ], + hdrs = [ + "upb/extension_registry.h", + ], + copts = UPB_DEFAULT_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":port", + ":table", + ], +) + cc_library( name = "mini_descriptor", srcs = [ @@ -150,8 +175,11 @@ cc_library( cc_library( name = "mini_table_internal", - hdrs = ["upb/msg_internal.h"], + hdrs = [ + "upb/msg_internal.h", + ], deps = [ + ":extension_registry", ":port", ":table", ":upb", @@ -170,6 +198,7 @@ cc_library( copts = UPB_DEFAULT_COPTS, visibility = ["//visibility:public"], deps = [ + ":extension_registry", ":mini_table_internal", ":port", ":upb", @@ -179,8 +208,8 @@ cc_library( cc_library( name = "mini_table_accessors", srcs = [ + "upb/internal/mini_table_accessors.h", "upb/mini_table_accessors.c", - "upb/mini_table_accessors_internal.h", ], hdrs = [ "upb/mini_table_accessors.h", @@ -198,10 +227,15 @@ cc_library( cc_test( name = "mini_table_test", - srcs = ["upb/mini_table_test.cc"], + srcs = [ + "upb/internal/table.h", + "upb/mini_table_test.cc", + ], deps = [ + ":extension_registry", ":mini_table", ":mini_table_internal", + ":port", ":upb", "@com_google_absl//absl/container:flat_hash_set", "@com_google_googletest//:gtest_main", @@ -233,13 +267,14 @@ cc_library( "upb/decode.h", "upb/decode_fast.c", "upb/decode_fast.h", - "upb/decode_internal.h", + "upb/internal/decode.h", + "upb/internal/upb.h", "upb/msg.h", "upb/msg_internal.h", - "upb/upb_internal.h", ], copts = UPB_DEFAULT_COPTS, deps = [ + ":extension_registry", ":port", ":table", "//third_party/utf8_range", @@ -258,6 +293,7 @@ cc_library( "upb/decode.h", "upb/decode_fast.h", "upb/encode.h", + "upb/extension_registry.h", "upb/msg.h", "upb/msg_internal.h", "upb/port_def.inc", @@ -302,10 +338,14 @@ upb_proto_reflection_library( cc_library( name = "collections", srcs = [ - "upb/collections.c", + "upb/array.c", + "upb/map.c", ], hdrs = [ + "upb/array.h", "upb/collections.h", + "upb/map.h", + "upb/message_value.h", ], copts = UPB_DEFAULT_COPTS, visibility = ["//visibility:public"], @@ -346,8 +386,8 @@ cc_library( cc_library( name = "textformat", srcs = [ + "upb/internal/upb.h", "upb/text_encode.c", - "upb/upb_internal.h", ], hdrs = [ "upb/text_encode.h", @@ -364,9 +404,9 @@ cc_library( cc_library( name = "json", srcs = [ + "upb/internal/upb.h", "upb/json_decode.c", "upb/json_encode.c", - "upb/upb_internal.h", ], hdrs = [ "upb/json_decode.h", @@ -667,6 +707,9 @@ sh_test( cc_library( name = "table", hdrs = [ + "upb/arena.h", + "upb/internal/table.h", + "upb/status.h", "upb/table_internal.h", "upb/upb.h", ], @@ -692,6 +735,7 @@ upb_amalgamation( libs = [ ":collections", ":descriptor_upb_proto", + ":extension_registry", ":fastdecode", ":mini_table", ":port", @@ -718,6 +762,7 @@ upb_amalgamation( ":collections", ":descriptor_upb_proto", ":descriptor_upb_proto_reflection", + ":extension_registry", ":fastdecode", ":json", ":mini_table", @@ -745,6 +790,7 @@ upb_amalgamation( libs = [ ":collections", ":descriptor_upb_proto", + ":extension_registry", ":fastdecode", ":json", ":mini_table", diff --git a/python/map.c b/python/map.c index a0ba2fe067..5e0f6abfbe 100644 --- a/python/map.c +++ b/python/map.c @@ -30,7 +30,7 @@ #include "python/convert.h" #include "python/message.h" #include "python/protobuf.h" -#include "upb/collections.h" +#include "upb/map.h" // ----------------------------------------------------------------------------- // MapContainer diff --git a/python/protobuf.h b/python/protobuf.h index be9eb817d3..2169a60e47 100644 --- a/python/protobuf.h +++ b/python/protobuf.h @@ -32,7 +32,6 @@ #include "python/descriptor.h" #include "python/python_api.h" -#include "upb/table_internal.h" // begin:github_only #define PYUPB_PROTOBUF_PUBLIC_PACKAGE "google.protobuf" diff --git a/upb/arena.c b/upb/arena.c new file mode 100644 index 0000000000..3c872698cd --- /dev/null +++ b/upb/arena.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/arena.h" + +#include + +#include "upb/internal/upb.h" + +// Must be last. +#include "upb/port_def.inc" + +/* 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); + } +} + +static uint32_t* upb_cleanup_pointer(uintptr_t cleanup_metadata) { + return (uint32_t*)(cleanup_metadata & ~0x1); +} + +static bool upb_cleanup_has_initial_block(uintptr_t cleanup_metadata) { + return cleanup_metadata & 0x1; +} + +static uintptr_t upb_cleanup_metadata(uint32_t* cleanup, + bool has_initial_block) { + return (uintptr_t)cleanup | has_initial_block; +} + +upb_alloc upb_alloc_global = {&upb_global_allocfunc}; + +/* upb_Arena ******************************************************************/ + +struct mem_block { + struct mem_block* next; + uint32_t size; + uint32_t cleanups; + /* Data follows. */ +}; + +typedef struct cleanup_ent { + upb_CleanupFunc* cleanup; + void* ud; +} cleanup_ent; + +static const size_t memblock_reserve = + UPB_ALIGN_UP(sizeof(mem_block), UPB_MALLOC_ALIGN); + +static upb_Arena* arena_findroot(upb_Arena* a) { + /* Path splitting keeps time complexity down, see: + * https://en.wikipedia.org/wiki/Disjoint-set_data_structure */ + while (a->parent != a) { + upb_Arena* next = a->parent; + a->parent = next->parent; + a = next; + } + return a; +} + +static void upb_Arena_addblock(upb_Arena* a, upb_Arena* root, void* ptr, + size_t size) { + mem_block* block = ptr; + + /* The block is for arena |a|, but should appear in the freelist of |root|. */ + block->next = root->freelist; + block->size = (uint32_t)size; + block->cleanups = 0; + root->freelist = block; + a->last_size = block->size; + if (!root->freelist_tail) root->freelist_tail = block; + + a->head.ptr = UPB_PTR_AT(block, memblock_reserve, char); + a->head.end = UPB_PTR_AT(block, size, char); + a->cleanup_metadata = upb_cleanup_metadata( + &block->cleanups, upb_cleanup_has_initial_block(a->cleanup_metadata)); + + UPB_POISON_MEMORY_REGION(a->head.ptr, a->head.end - a->head.ptr); +} + +static bool upb_Arena_Allocblock(upb_Arena* a, size_t size) { + upb_Arena* root = arena_findroot(a); + size_t block_size = UPB_MAX(size, a->last_size * 2) + memblock_reserve; + mem_block* block = upb_malloc(root->block_alloc, block_size); + + if (!block) return false; + upb_Arena_addblock(a, root, block, block_size); + return true; +} + +void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size) { + if (!upb_Arena_Allocblock(a, size)) return NULL; /* Out of memory. */ + UPB_ASSERT(_upb_ArenaHas(a) >= size); + 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. */ + return upb_Arena_Realloc(a, ptr, oldsize, size); +} + +/* Public Arena API ***********************************************************/ + +upb_Arena* arena_initslow(void* mem, size_t n, upb_alloc* alloc) { + const size_t first_block_overhead = sizeof(upb_Arena) + memblock_reserve; + upb_Arena* a; + + /* We need to malloc the initial block. */ + n = first_block_overhead + 256; + if (!alloc || !(mem = upb_malloc(alloc, n))) { + return NULL; + } + + a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena); + n -= sizeof(*a); + + a->head.alloc.func = &upb_Arena_doalloc; + a->block_alloc = alloc; + a->parent = a; + a->refcount = 1; + a->freelist = NULL; + a->freelist_tail = NULL; + a->cleanup_metadata = upb_cleanup_metadata(NULL, false); + + upb_Arena_addblock(a, a, mem, n); + + return a; +} + +upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc) { + upb_Arena* a; + + if (n) { + /* Align initial pointer up so that we return properly-aligned pointers. */ + void* aligned = (void*)UPB_ALIGN_UP((uintptr_t)mem, UPB_MALLOC_ALIGN); + size_t delta = (uintptr_t)aligned - (uintptr_t)mem; + n = delta <= n ? n - delta : 0; + mem = aligned; + } + + /* Round block size down to alignof(*a) since we will allocate the arena + * itself at the end. */ + n = UPB_ALIGN_DOWN(n, UPB_ALIGN_OF(upb_Arena)); + + if (UPB_UNLIKELY(n < sizeof(upb_Arena))) { + return arena_initslow(mem, n, alloc); + } + + a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena); + + a->head.alloc.func = &upb_Arena_doalloc; + a->block_alloc = alloc; + a->parent = a; + a->refcount = 1; + a->last_size = UPB_MAX(128, n); + a->head.ptr = mem; + a->head.end = UPB_PTR_AT(mem, n - sizeof(*a), char); + a->freelist = NULL; + a->cleanup_metadata = upb_cleanup_metadata(NULL, true); + + return a; +} + +static void arena_dofree(upb_Arena* a) { + mem_block* block = a->freelist; + UPB_ASSERT(a->parent == a); + UPB_ASSERT(a->refcount == 0); + + while (block) { + /* Load first since we are deleting block. */ + mem_block* next = block->next; + + if (block->cleanups > 0) { + cleanup_ent* end = UPB_PTR_AT(block, block->size, void); + cleanup_ent* ptr = end - block->cleanups; + + for (; ptr < end; ptr++) { + ptr->cleanup(ptr->ud); + } + } + + upb_free(a->block_alloc, block); + block = next; + } +} + +void upb_Arena_Free(upb_Arena* a) { + a = arena_findroot(a); + if (--a->refcount == 0) arena_dofree(a); +} + +bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func) { + cleanup_ent* ent; + uint32_t* cleanups = upb_cleanup_pointer(a->cleanup_metadata); + + if (!cleanups || _upb_ArenaHas(a) < sizeof(cleanup_ent)) { + if (!upb_Arena_Allocblock(a, 128)) return false; /* Out of memory. */ + UPB_ASSERT(_upb_ArenaHas(a) >= sizeof(cleanup_ent)); + cleanups = upb_cleanup_pointer(a->cleanup_metadata); + } + + a->head.end -= sizeof(cleanup_ent); + ent = (cleanup_ent*)a->head.end; + (*cleanups)++; + UPB_UNPOISON_MEMORY_REGION(ent, sizeof(cleanup_ent)); + + ent->cleanup = func; + ent->ud = ud; + + return true; +} + +bool upb_Arena_Fuse(upb_Arena* a1, upb_Arena* a2) { + upb_Arena* r1 = arena_findroot(a1); + upb_Arena* r2 = arena_findroot(a2); + + if (r1 == r2) return true; /* Already fused. */ + + /* Do not fuse initial blocks since we cannot lifetime extend them. */ + if (upb_cleanup_has_initial_block(r1->cleanup_metadata)) return false; + if (upb_cleanup_has_initial_block(r2->cleanup_metadata)) return false; + + /* Only allow fuse with a common allocator */ + if (r1->block_alloc != r2->block_alloc) return false; + + /* We want to join the smaller tree to the larger tree. + * So swap first if they are backwards. */ + if (r1->refcount < r2->refcount) { + upb_Arena* tmp = r1; + r1 = r2; + r2 = tmp; + } + + /* r1 takes over r2's freelist and refcount. */ + r1->refcount += r2->refcount; + if (r2->freelist_tail) { + UPB_ASSERT(r2->freelist_tail->next == NULL); + r2->freelist_tail->next = r1->freelist; + r1->freelist = r2->freelist; + } + r2->parent = r1; + return true; +} diff --git a/upb/arena.h b/upb/arena.h new file mode 100644 index 0000000000..0c4fd1b83c --- /dev/null +++ b/upb/arena.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_ARENA_H_ +#define UPB_ARENA_H_ + +#include +#include +#include +#include + +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +/** upb_alloc *****************************************************************/ + +/* A upb_alloc is a possibly-stateful allocator object. + * + * It could either be an arena allocator (which doesn't require individual + * free() calls) or a regular malloc() (which does). The client must therefore + * free memory unless it knows that the allocator is an arena allocator. */ + +struct upb_alloc; +typedef struct upb_alloc upb_alloc; + +/* A malloc()/free() function. + * If "size" is 0 then the function acts like free(), otherwise it acts like + * realloc(). Only "oldsize" bytes from a previous allocation are preserved. */ +typedef void* upb_alloc_func(upb_alloc* alloc, void* ptr, size_t oldsize, + size_t size); + +struct upb_alloc { + upb_alloc_func* func; +}; + +UPB_INLINE void* upb_malloc(upb_alloc* alloc, size_t size) { + UPB_ASSERT(alloc); + return alloc->func(alloc, NULL, 0, size); +} + +UPB_INLINE void* upb_realloc(upb_alloc* alloc, void* ptr, size_t oldsize, + size_t size) { + UPB_ASSERT(alloc); + return alloc->func(alloc, ptr, oldsize, size); +} + +UPB_INLINE void upb_free(upb_alloc* alloc, void* ptr) { + assert(alloc); + alloc->func(alloc, ptr, 0, 0); +} + +/* The global allocator used by upb. Uses the standard malloc()/free(). */ + +extern upb_alloc upb_alloc_global; + +/* Functions that hard-code the global malloc. + * + * We still get benefit because we can put custom logic into our global + * allocator, like injecting out-of-memory faults in debug/testing builds. */ + +UPB_INLINE void* upb_gmalloc(size_t size) { + return upb_malloc(&upb_alloc_global, size); +} + +UPB_INLINE void* upb_grealloc(void* ptr, size_t oldsize, size_t size) { + return upb_realloc(&upb_alloc_global, ptr, oldsize, size); +} + +UPB_INLINE void upb_gfree(void* ptr) { upb_free(&upb_alloc_global, ptr); } + +/* upb_Arena ******************************************************************/ + +/* upb_Arena is a specific allocator implementation that uses arena allocation. + * The user provides an allocator that will be used to allocate the underlying + * arena blocks. Arenas by nature do not require the individual allocations + * to be freed. However the Arena does allow users to register cleanup + * functions that will run when the arena is destroyed. + * + * A upb_Arena is *not* thread-safe. + * + * You could write a thread-safe arena allocator that satisfies the + * upb_alloc interface, but it would not be as efficient for the + * single-threaded case. */ + +typedef void upb_CleanupFunc(void* ud); + +struct upb_Arena; +typedef struct upb_Arena upb_Arena; + +typedef struct { + /* We implement the allocator interface. + * This must be the first member of upb_Arena! + * TODO(haberman): remove once handlers are gone. */ + upb_alloc alloc; + + char *ptr, *end; +} _upb_ArenaHead; + +/* Creates an arena from the given initial block (if any -- n may be 0). + * Additional blocks will be allocated from |alloc|. If |alloc| is NULL, this + * is a fixed-size arena and cannot grow. */ +upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc); +void upb_Arena_Free(upb_Arena* a); +bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func); +bool upb_Arena_Fuse(upb_Arena* a, upb_Arena* b); +void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size); + +UPB_INLINE upb_alloc* upb_Arena_Alloc(upb_Arena* a) { return (upb_alloc*)a; } + +UPB_INLINE size_t _upb_ArenaHas(upb_Arena* a) { + _upb_ArenaHead* h = (_upb_ArenaHead*)a; + return (size_t)(h->end - h->ptr); +} + +UPB_INLINE void* _upb_Arena_FastMalloc(upb_Arena* a, size_t size) { + _upb_ArenaHead* h = (_upb_ArenaHead*)a; + void* ret = h->ptr; + UPB_ASSERT(UPB_ALIGN_MALLOC((uintptr_t)ret) == (uintptr_t)ret); + UPB_ASSERT(UPB_ALIGN_MALLOC(size) == size); + UPB_UNPOISON_MEMORY_REGION(ret, size); + + h->ptr += size; + +#if UPB_ASAN + { + size_t guard_size = 32; + if (_upb_ArenaHas(a) >= guard_size) { + h->ptr += guard_size; + } else { + h->ptr = h->end; + } + } +#endif + + return ret; +} + +UPB_INLINE void* upb_Arena_Malloc(upb_Arena* a, size_t size) { + size = UPB_ALIGN_MALLOC(size); + + if (UPB_UNLIKELY(_upb_ArenaHas(a) < size)) { + return _upb_Arena_SlowMalloc(a, size); + } + + return _upb_Arena_FastMalloc(a, size); +} + +// Shrinks the last alloc from arena. +// REQUIRES: (ptr, oldsize) was the last malloc/realloc from this arena. +// We could also add a upb_Arena_TryShrinkLast() which is simply a no-op if +// this was not the last alloc. +UPB_INLINE void upb_Arena_ShrinkLast(upb_Arena* a, void* ptr, size_t oldsize, + size_t size) { + _upb_ArenaHead* h = (_upb_ArenaHead*)a; + oldsize = UPB_ALIGN_MALLOC(oldsize); + size = UPB_ALIGN_MALLOC(size); + UPB_ASSERT((char*)ptr + oldsize == h->ptr); // Must be the last alloc. + UPB_ASSERT(size <= oldsize); + h->ptr = (char*)ptr + size; +} + +UPB_INLINE void* upb_Arena_Realloc(upb_Arena* a, void* ptr, size_t oldsize, + size_t size) { + _upb_ArenaHead* h = (_upb_ArenaHead*)a; + oldsize = UPB_ALIGN_MALLOC(oldsize); + size = UPB_ALIGN_MALLOC(size); + bool is_most_recent_alloc = (uintptr_t)ptr + oldsize == (uintptr_t)h->ptr; + + if (is_most_recent_alloc) { + ptrdiff_t diff = size - oldsize; + if ((ptrdiff_t)_upb_ArenaHas(a) >= diff) { + h->ptr += diff; + return ptr; + } + } else if (size <= oldsize) { + return ptr; + } + + void* ret = upb_Arena_Malloc(a, size); + + if (ret && oldsize > 0) { + memcpy(ret, ptr, UPB_MIN(oldsize, size)); + } + + return ret; +} + +UPB_INLINE upb_Arena* upb_Arena_New(void) { + return upb_Arena_Init(NULL, 0, &upb_alloc_global); +} + +#include "upb/port_undef.inc" + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* UPB_ARENA_H_ */ diff --git a/upb/array.c b/upb/array.c new file mode 100644 index 0000000000..b4cdc49924 --- /dev/null +++ b/upb/array.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/array.h" + +#include + +#include "upb/internal/table.h" +#include "upb/msg.h" +#include "upb/port_def.inc" + +static const char _upb_CTypeo_sizelg2[12] = { + 0, + 0, /* kUpb_CType_Bool */ + 2, /* kUpb_CType_Float */ + 2, /* kUpb_CType_Int32 */ + 2, /* kUpb_CType_UInt32 */ + 2, /* kUpb_CType_Enum */ + UPB_SIZE(2, 3), /* kUpb_CType_Message */ + 3, /* kUpb_CType_Double */ + 3, /* kUpb_CType_Int64 */ + 3, /* kUpb_CType_UInt64 */ + UPB_SIZE(3, 4), /* kUpb_CType_String */ + UPB_SIZE(3, 4), /* kUpb_CType_Bytes */ +}; + +upb_Array* upb_Array_New(upb_Arena* a, upb_CType type) { + return _upb_Array_New(a, 4, _upb_CTypeo_sizelg2[type]); +} + +size_t upb_Array_Size(const upb_Array* arr) { return arr->len; } + +upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i) { + upb_MessageValue ret; + const char* data = _upb_array_constptr(arr); + int lg2 = arr->data & 7; + UPB_ASSERT(i < arr->len); + memcpy(&ret, data + (i << lg2), 1 << lg2); + return ret; +} + +void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val) { + char* data = _upb_array_ptr(arr); + int lg2 = arr->data & 7; + UPB_ASSERT(i < arr->len); + memcpy(data + (i << lg2), &val, 1 << lg2); +} + +bool upb_Array_Append(upb_Array* arr, upb_MessageValue val, upb_Arena* arena) { + if (!upb_Array_Resize(arr, arr->len + 1, arena)) { + return false; + } + upb_Array_Set(arr, arr->len - 1, val); + return true; +} + +void upb_Array_Move(upb_Array* arr, size_t dst_idx, size_t src_idx, + size_t count) { + char* data = _upb_array_ptr(arr); + int lg2 = arr->data & 7; + memmove(&data[dst_idx << lg2], &data[src_idx << lg2], count << lg2); +} + +bool upb_Array_Insert(upb_Array* arr, size_t i, size_t count, + upb_Arena* arena) { + UPB_ASSERT(i <= arr->len); + UPB_ASSERT(count + arr->len >= count); + size_t oldsize = arr->len; + if (!upb_Array_Resize(arr, arr->len + count, arena)) { + return false; + } + upb_Array_Move(arr, i + count, i, oldsize - i); + return true; +} + +/* + * i end arr->len + * |------------|XXXXXXXX|--------| + */ +void upb_Array_Delete(upb_Array* arr, size_t i, size_t count) { + size_t end = i + count; + UPB_ASSERT(i <= end); + UPB_ASSERT(end <= arr->len); + upb_Array_Move(arr, i, end, arr->len - end); + arr->len -= count; +} + +bool upb_Array_Resize(upb_Array* arr, size_t size, upb_Arena* arena) { + return _upb_Array_Resize(arr, size, arena); +} diff --git a/upb/array.h b/upb/array.h new file mode 100644 index 0000000000..0715d58430 --- /dev/null +++ b/upb/array.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_ARRAY_H_ +#define UPB_ARRAY_H_ + +#include "google/protobuf/descriptor.upb.h" +#include "upb/message_value.h" + +// Must be last. +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Creates a new array on the given arena that holds elements of this type. */ +upb_Array* upb_Array_New(upb_Arena* a, upb_CType type); + +/* Returns the size of the array. */ +size_t upb_Array_Size(const upb_Array* arr); + +/* Returns the given element, which must be within the array's current size. */ +upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i); + +/* Sets the given element, which must be within the array's current size. */ +void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val); + +/* Appends an element to the array. Returns false on allocation failure. */ +bool upb_Array_Append(upb_Array* array, upb_MessageValue val, upb_Arena* arena); + +/* Moves elements within the array using memmove(). Like memmove(), the source + * and destination elements may be overlapping. */ +void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx, + size_t count); + +/* Inserts one or more empty elements into the array. Existing elements are + * shifted right. The new elements have undefined state and must be set with + * `upb_Array_Set()`. + * REQUIRES: `i <= upb_Array_Size(arr)` */ +bool upb_Array_Insert(upb_Array* array, size_t i, size_t count, + upb_Arena* arena); + +/* Deletes one or more elements from the array. Existing elements are shifted + * left. + * REQUIRES: `i + count <= upb_Array_Size(arr)` */ +void upb_Array_Delete(upb_Array* array, size_t i, size_t count); + +/* Changes the size of a vector. New elements are initialized to empty/0. + * Returns false on allocation failure. */ +bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif /* UPB_ARRAY_H_ */ diff --git a/upb/collections.h b/upb/collections.h index 047e14ef68..81558dc5c9 100644 --- a/upb/collections.h +++ b/upb/collections.h @@ -28,147 +28,9 @@ #ifndef UPB_COLLECTIONS_H_ #define UPB_COLLECTIONS_H_ -#include "google/protobuf/descriptor.upb.h" -#include "upb/port_def.inc" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef union { - bool bool_val; - float float_val; - double double_val; - int32_t int32_val; - int64_t int64_val; - uint32_t uint32_val; - uint64_t uint64_val; - const upb_Map* map_val; - const upb_Message* msg_val; - const upb_Array* array_val; - upb_StringView str_val; -} upb_MessageValue; - -typedef union { - upb_Map* map; - upb_Message* msg; - upb_Array* array; -} upb_MutableMessageValue; - -/** upb_Array *****************************************************************/ - -/* Creates a new array on the given arena that holds elements of this type. */ -upb_Array* upb_Array_New(upb_Arena* a, upb_CType type); - -/* Returns the size of the array. */ -size_t upb_Array_Size(const upb_Array* arr); - -/* Returns the given element, which must be within the array's current size. */ -upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i); - -/* Sets the given element, which must be within the array's current size. */ -void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val); - -/* Appends an element to the array. Returns false on allocation failure. */ -bool upb_Array_Append(upb_Array* array, upb_MessageValue val, upb_Arena* arena); - -/* Moves elements within the array using memmove(). Like memmove(), the source - * and destination elements may be overlapping. */ -void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx, - size_t count); - -/* Inserts one or more empty elements into the array. Existing elements are - * shifted right. The new elements have undefined state and must be set with - * `upb_Array_Set()`. - * REQUIRES: `i <= upb_Array_Size(arr)` */ -bool upb_Array_Insert(upb_Array* array, size_t i, size_t count, - upb_Arena* arena); - -/* Deletes one or more elements from the array. Existing elements are shifted - * left. - * REQUIRES: `i + count <= upb_Array_Size(arr)` */ -void upb_Array_Delete(upb_Array* array, size_t i, size_t count); - -/* Changes the size of a vector. New elements are initialized to empty/0. - * Returns false on allocation failure. */ -bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena); - -/** upb_Map *******************************************************************/ - -/* Creates a new map on the given arena with the given key/value size. */ -upb_Map* upb_Map_New(upb_Arena* a, upb_CType key_type, upb_CType value_type); - -/* Returns the number of entries in the map. */ -size_t upb_Map_Size(const upb_Map* map); - -/* Stores a value for the given key into |*val| (or the zero value if the key is - * not present). Returns whether the key was present. The |val| pointer may be - * NULL, in which case the function tests whether the given key is present. */ -bool upb_Map_Get(const upb_Map* map, upb_MessageValue key, - upb_MessageValue* val); - -/* Removes all entries in the map. */ -void upb_Map_Clear(upb_Map* map); - -typedef enum { - // LINT.IfChange - kUpb_MapInsertStatus_Inserted = 0, - kUpb_MapInsertStatus_Replaced = 1, - kUpb_MapInsertStatus_OutOfMemory = 2, - // LINT.ThenChange(//depot/google3/third_party/upb/upb/msg_internal.h) -} upb_MapInsertStatus; - -/* Sets the given key to the given value, returning whether the key was inserted - * or replaced. If the key was inserted, then any existing iterators will be - * invalidated. */ -upb_MapInsertStatus upb_Map_Insert(upb_Map* map, upb_MessageValue key, - upb_MessageValue val, upb_Arena* arena); - -/* Sets the given key to the given value. Returns false if memory allocation - * failed. If the key is newly inserted, then any existing iterators will be - * invalidated. */ -UPB_INLINE bool upb_Map_Set(upb_Map* map, upb_MessageValue key, - upb_MessageValue val, upb_Arena* arena) { - return upb_Map_Insert(map, key, val, arena) != - kUpb_MapInsertStatus_OutOfMemory; -} - -/* Deletes this key from the table. Returns true if the key was present. */ -bool upb_Map_Delete(upb_Map* map, upb_MessageValue key); - -/* Map iteration: - * - * size_t iter = kUpb_Map_Begin; - * while (upb_MapIterator_Next(map, &iter)) { - * upb_MessageValue key = upb_MapIterator_Key(map, iter); - * upb_MessageValue val = upb_MapIterator_Value(map, iter); - * - * // If mutating is desired. - * upb_MapIterator_SetValue(map, iter, value2); - * } - */ - -/* Advances to the next entry. Returns false if no more entries are present. */ -bool upb_MapIterator_Next(const upb_Map* map, size_t* iter); - -/* Returns true if the iterator still points to a valid entry, or false if the - * iterator is past the last element. It is an error to call this function with - * kUpb_Map_Begin (you must call next() at least once first). */ -bool upb_MapIterator_Done(const upb_Map* map, size_t iter); - -/* Returns the key and value for this entry of the map. */ -upb_MessageValue upb_MapIterator_Key(const upb_Map* map, size_t iter); -upb_MessageValue upb_MapIterator_Value(const upb_Map* map, size_t iter); - -/* Sets the value for this entry. The iterator must not be done, and the - * iterator must not have been initialized const. */ -void upb_MapIterator_SetValue(upb_Map* map, size_t iter, - upb_MessageValue value); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#include "upb/port_undef.inc" +// TODO(b/232091617): Delete this entire header which currently exists only for +// temporary backwards compatibility. +#include "upb/array.h" +#include "upb/map.h" #endif /* UPB_COLLECTIONS_H_ */ diff --git a/upb/decode.c b/upb/decode.c index e6dac273c3..d314875313 100644 --- a/upb/decode.c +++ b/upb/decode.c @@ -30,9 +30,9 @@ #include #include -#include "upb/decode_internal.h" +#include "upb/internal/decode.h" +#include "upb/internal/upb.h" #include "upb/upb.h" -#include "upb/upb_internal.h" /* Must be last. */ #include "upb/port_def.inc" diff --git a/upb/decode.h b/upb/decode.h index 1fa8131b7d..0c09497feb 100644 --- a/upb/decode.h +++ b/upb/decode.h @@ -32,6 +32,7 @@ #ifndef UPB_DECODE_H_ #define UPB_DECODE_H_ +#include "upb/extension_registry.h" #include "upb/msg.h" /* Must be last. */ diff --git a/upb/decode_fast.c b/upb/decode_fast.c index e05bf8e0db..a02ad0948f 100644 --- a/upb/decode_fast.c +++ b/upb/decode_fast.c @@ -37,7 +37,7 @@ #include "upb/decode_fast.h" -#include "upb/decode_internal.h" +#include "upb/internal/decode.h" /* Must be last. */ #include "upb/port_def.inc" diff --git a/upb/decode_internal.h b/upb/decode_internal.h index 11bb9aa095..fe3cbc7d49 100644 --- a/upb/decode_internal.h +++ b/upb/decode_internal.h @@ -25,187 +25,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - * Internal implementation details of the decoder that are shared between - * decode.c and decode_fast.c. - */ - #ifndef UPB_DECODE_INT_H_ #define UPB_DECODE_INT_H_ -#include - -#include "third_party/utf8_range/utf8_range.h" -#include "upb/decode.h" -#include "upb/msg_internal.h" -#include "upb/upb_internal.h" - -/* Must be last. */ -#include "upb/port_def.inc" - -#define DECODE_NOGROUP (uint32_t) - 1 - -typedef struct upb_Decoder { - const char* end; /* Can read up to 16 bytes slop beyond this. */ - const char* limit_ptr; /* = end + UPB_MIN(limit, 0) */ - upb_Message* unknown_msg; /* If non-NULL, add unknown data at buffer flip. */ - const char* unknown; /* Start of unknown data. */ - const upb_ExtensionRegistry* - extreg; /* For looking up extensions during the parse. */ - int limit; /* Submessage limit relative to end. */ - int depth; /* Tracks recursion depth to bound stack usage. */ - uint32_t end_group; /* field number of END_GROUP tag, else DECODE_NOGROUP */ - uint16_t options; - bool missing_required; - char patch[32]; - upb_Arena arena; - jmp_buf err; - -#ifndef NDEBUG - const char* debug_tagstart; - const char* debug_valstart; -#endif -} upb_Decoder; - -/* Error function that will abort decoding with longjmp(). We can't declare this - * UPB_NORETURN, even though it is appropriate, because if we do then compilers - * will "helpfully" refuse to tailcall to it - * (see: https://stackoverflow.com/a/55657013), which will defeat a major goal - * of our optimizations. That is also why we must declare it in a separate file, - * otherwise the compiler will see that it calls longjmp() and deduce that it is - * noreturn. */ -const char* fastdecode_err(upb_Decoder* d, int status); - -extern const uint8_t upb_utf8_offsets[]; - -UPB_INLINE -bool decode_verifyutf8_inl(const char* ptr, int len) { - const char* end = ptr + len; - - // Check 8 bytes at a time for any non-ASCII char. - while (end - ptr >= 8) { - uint64_t data; - memcpy(&data, ptr, 8); - if (data & 0x8080808080808080) goto non_ascii; - ptr += 8; - } - - // Check one byte at a time for non-ASCII. - while (ptr < end) { - if (*ptr & 0x80) goto non_ascii; - ptr++; - } - - return true; - -non_ascii: - return utf8_range2((const unsigned char*)ptr, end - ptr) == 0; -} - -const char* decode_checkrequired(upb_Decoder* d, const char* ptr, - const upb_Message* msg, - const upb_MiniTable* l); - -/* x86-64 pointers always have the high 16 bits matching. So we can shift - * left 8 and right 8 without loss of information. */ -UPB_INLINE intptr_t decode_totable(const upb_MiniTable* tablep) { - return ((intptr_t)tablep << 8) | tablep->table_mask; -} - -UPB_INLINE const upb_MiniTable* decode_totablep(intptr_t table) { - return (const upb_MiniTable*)(table >> 8); -} - -UPB_INLINE -const char* decode_isdonefallback_inl(upb_Decoder* d, const char* ptr, - int overrun, int* status) { - if (overrun < d->limit) { - /* Need to copy remaining data into patch buffer. */ - UPB_ASSERT(overrun < 16); - if (d->unknown_msg) { - if (!_upb_Message_AddUnknown(d->unknown_msg, d->unknown, ptr - d->unknown, - &d->arena)) { - *status = kUpb_DecodeStatus_OutOfMemory; - return NULL; - } - d->unknown = &d->patch[0] + overrun; - } - memset(d->patch + 16, 0, 16); - memcpy(d->patch, d->end, 16); - ptr = &d->patch[0] + overrun; - d->end = &d->patch[16]; - d->limit -= 16; - d->limit_ptr = d->end + d->limit; - d->options &= ~kUpb_DecodeOption_AliasString; - UPB_ASSERT(ptr < d->limit_ptr); - return ptr; - } else { - *status = kUpb_DecodeStatus_Malformed; - return NULL; - } -} - -const char* decode_isdonefallback(upb_Decoder* d, const char* ptr, int overrun); - -UPB_INLINE -bool decode_isdone(upb_Decoder* d, const char** ptr) { - int overrun = *ptr - d->end; - if (UPB_LIKELY(*ptr < d->limit_ptr)) { - return false; - } else if (UPB_LIKELY(overrun == d->limit)) { - return true; - } else { - *ptr = decode_isdonefallback(d, *ptr, overrun); - return false; - } -} - -#if UPB_FASTTABLE -UPB_INLINE -const char* fastdecode_tagdispatch(upb_Decoder* d, const char* ptr, - upb_Message* msg, intptr_t table, - uint64_t hasbits, uint64_t tag) { - const upb_MiniTable* table_p = decode_totablep(table); - uint8_t mask = table; - uint64_t data; - size_t idx = tag & mask; - UPB_ASSUME((idx & 7) == 0); - idx >>= 3; - data = table_p->fasttable[idx].field_data ^ tag; - UPB_MUSTTAIL return table_p->fasttable[idx].field_parser(d, ptr, msg, table, - hasbits, data); -} -#endif - -UPB_INLINE uint32_t fastdecode_loadtag(const char* ptr) { - uint16_t tag; - memcpy(&tag, ptr, 2); - return tag; -} - -UPB_INLINE void decode_checklimit(upb_Decoder* d) { - UPB_ASSERT(d->limit_ptr == d->end + UPB_MIN(0, d->limit)); -} - -UPB_INLINE int decode_pushlimit(upb_Decoder* d, const char* ptr, int size) { - int limit = size + (int)(ptr - d->end); - int delta = d->limit - limit; - decode_checklimit(d); - d->limit = limit; - d->limit_ptr = d->end + UPB_MIN(0, limit); - decode_checklimit(d); - return delta; -} - -UPB_INLINE void decode_poplimit(upb_Decoder* d, const char* ptr, - int saved_delta) { - UPB_ASSERT(ptr - d->end == d->limit); - decode_checklimit(d); - d->limit += saved_delta; - d->limit_ptr = d->end + UPB_MIN(0, d->limit); - decode_checklimit(d); -} +// TODO(b/232091617): Delete this entire header which currently exists only for +// temporary backwards compatibility. -#include "upb/port_undef.inc" +#include "upb/internal/decode.h" #endif /* UPB_DECODE_INT_H_ */ diff --git a/upb/def.h b/upb/def.h index c520f858fa..2cb312df1a 100644 --- a/upb/def.h +++ b/upb/def.h @@ -29,7 +29,7 @@ #define UPB_DEF_H_ #include "google/protobuf/descriptor.upb.h" -#include "upb/table_internal.h" +#include "upb/internal/table.h" #include "upb/upb.h" /* Must be last. */ diff --git a/upb/encode.c b/upb/encode.c index c917397b30..048ecd2396 100644 --- a/upb/encode.c +++ b/upb/encode.c @@ -32,6 +32,7 @@ #include #include +#include "upb/extension_registry.h" #include "upb/msg_internal.h" #include "upb/upb.h" diff --git a/upb/extension_registry.c b/upb/extension_registry.c new file mode 100644 index 0000000000..921cf227dd --- /dev/null +++ b/upb/extension_registry.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/extension_registry.h" + +#include "upb/internal/table.h" +#include "upb/msg.h" +#include "upb/msg_internal.h" + +// Must be last. +#include "upb/port_def.inc" + +struct upb_ExtensionRegistry { + upb_Arena* arena; + upb_strtable exts; /* Key is upb_MiniTable* concatenated with fieldnum. */ +}; + +#define EXTREG_KEY_SIZE (sizeof(upb_MiniTable*) + sizeof(uint32_t)) + +static void extreg_key(char* buf, const upb_MiniTable* l, uint32_t fieldnum) { + memcpy(buf, &l, sizeof(l)); + memcpy(buf + sizeof(l), &fieldnum, sizeof(fieldnum)); +} + +upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena) { + upb_ExtensionRegistry* r = upb_Arena_Malloc(arena, sizeof(*r)); + if (!r) return NULL; + r->arena = arena; + if (!upb_strtable_init(&r->exts, 8, arena)) return NULL; + return r; +} + +bool _upb_extreg_add(upb_ExtensionRegistry* r, + const upb_MiniTable_Extension** e, size_t count) { + char buf[EXTREG_KEY_SIZE]; + const upb_MiniTable_Extension** start = e; + const upb_MiniTable_Extension** end = UPB_PTRADD(e, count); + for (; e < end; e++) { + const upb_MiniTable_Extension* ext = *e; + extreg_key(buf, ext->extendee, ext->field.number); + if (!upb_strtable_insert(&r->exts, buf, EXTREG_KEY_SIZE, + upb_value_constptr(ext), r->arena)) { + goto failure; + } + } + return true; + +failure: + /* Back out the entries previously added. */ + for (end = e, e = start; e < end; e++) { + const upb_MiniTable_Extension* ext = *e; + extreg_key(buf, ext->extendee, ext->field.number); + upb_strtable_remove2(&r->exts, buf, EXTREG_KEY_SIZE, NULL); + } + return false; +} + +const upb_MiniTable_Extension* _upb_extreg_get(const upb_ExtensionRegistry* r, + const upb_MiniTable* l, + uint32_t num) { + char buf[EXTREG_KEY_SIZE]; + upb_value v; + extreg_key(buf, l, num); + if (upb_strtable_lookup2(&r->exts, buf, EXTREG_KEY_SIZE, &v)) { + return upb_value_getconstptr(v); + } else { + return NULL; + } +} diff --git a/upb/extension_registry.h b/upb/extension_registry.h new file mode 100644 index 0000000000..0e0a4440ca --- /dev/null +++ b/upb/extension_registry.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_EXTENSION_REGISTRY_H_ +#define UPB_EXTENSION_REGISTRY_H_ + +#include + +#include "upb/upb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Extension registry: a dynamic data structure that stores a map of: + * (upb_MiniTable, number) -> extension info + * + * upb_decode() uses upb_ExtensionRegistry to look up extensions while parsing + * binary format. + * + * upb_ExtensionRegistry is part of the mini-table (msglayout) family of + * objects. Like all mini-table objects, it is suitable for reflection-less + * builds that do not want to expose names into the binary. + * + * Unlike most mini-table types, upb_ExtensionRegistry requires dynamic memory + * allocation and dynamic initialization: + * * If reflection is being used, then upb_DefPool will construct an appropriate + * upb_ExtensionRegistry automatically. + * * For a mini-table only build, the user must manually construct the + * upb_ExtensionRegistry and populate it with all of the extensions the user + * cares about. + * * A third alternative is to manually unpack relevant extensions after the + * main parse is complete, similar to how Any works. This is perhaps the + * nicest solution from the perspective of reducing dependencies, avoiding + * dynamic memory allocation, and avoiding the need to parse uninteresting + * extensions. The downsides are: + * (1) parse errors are not caught during the main parse + * (2) the CPU hit of parsing comes during access, which could cause an + * undesirable stutter in application performance. + * + * Users cannot directly get or put into this map. Users can only add the + * extensions from a generated module and pass the extension registry to the + * binary decoder. + * + * A upb_DefPool provides a upb_ExtensionRegistry, so any users who use + * reflection do not need to populate a upb_ExtensionRegistry directly. + */ + +struct upb_ExtensionRegistry; +typedef struct upb_ExtensionRegistry upb_ExtensionRegistry; + +/* Creates a upb_ExtensionRegistry in the given arena. The arena must outlive + * any use of the extreg. */ +upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* UPB_EXTENSION_REGISTRY_H_ */ diff --git a/upb/internal/decode.h b/upb/internal/decode.h new file mode 100644 index 0000000000..708874c9c7 --- /dev/null +++ b/upb/internal/decode.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Internal implementation details of the decoder that are shared between + * decode.c and decode_fast.c. + */ + +#ifndef UPB_INTERNAL_DECODE_H_ +#define UPB_INTERNAL_DECODE_H_ + +#include + +#include "upb/decode.h" +#include "upb/internal/upb.h" +#include "upb/msg_internal.h" +#include "third_party/utf8_range/utf8_range.h" + +/* Must be last. */ +#include "upb/port_def.inc" + +#define DECODE_NOGROUP (uint32_t) - 1 + +typedef struct upb_Decoder { + const char* end; /* Can read up to 16 bytes slop beyond this. */ + const char* limit_ptr; /* = end + UPB_MIN(limit, 0) */ + upb_Message* unknown_msg; /* If non-NULL, add unknown data at buffer flip. */ + const char* unknown; /* Start of unknown data. */ + const upb_ExtensionRegistry* + extreg; /* For looking up extensions during the parse. */ + int limit; /* Submessage limit relative to end. */ + int depth; /* Tracks recursion depth to bound stack usage. */ + uint32_t end_group; /* field number of END_GROUP tag, else DECODE_NOGROUP */ + uint16_t options; + bool missing_required; + char patch[32]; + upb_Arena arena; + jmp_buf err; + +#ifndef NDEBUG + const char* debug_tagstart; + const char* debug_valstart; +#endif +} upb_Decoder; + +/* Error function that will abort decoding with longjmp(). We can't declare this + * UPB_NORETURN, even though it is appropriate, because if we do then compilers + * will "helpfully" refuse to tailcall to it + * (see: https://stackoverflow.com/a/55657013), which will defeat a major goal + * of our optimizations. That is also why we must declare it in a separate file, + * otherwise the compiler will see that it calls longjmp() and deduce that it is + * noreturn. */ +const char* fastdecode_err(upb_Decoder* d, int status); + +extern const uint8_t upb_utf8_offsets[]; + +UPB_INLINE +bool decode_verifyutf8_inl(const char* ptr, int len) { + const char* end = ptr + len; + + // Check 8 bytes at a time for any non-ASCII char. + while (end - ptr >= 8) { + uint64_t data; + memcpy(&data, ptr, 8); + if (data & 0x8080808080808080) goto non_ascii; + ptr += 8; + } + + // Check one byte at a time for non-ASCII. + while (ptr < end) { + if (*ptr & 0x80) goto non_ascii; + ptr++; + } + + return true; + +non_ascii: + return utf8_range2((const unsigned char*)ptr, end - ptr) == 0; +} + +const char* decode_checkrequired(upb_Decoder* d, const char* ptr, + const upb_Message* msg, + const upb_MiniTable* l); + +/* x86-64 pointers always have the high 16 bits matching. So we can shift + * left 8 and right 8 without loss of information. */ +UPB_INLINE intptr_t decode_totable(const upb_MiniTable* tablep) { + return ((intptr_t)tablep << 8) | tablep->table_mask; +} + +UPB_INLINE const upb_MiniTable* decode_totablep(intptr_t table) { + return (const upb_MiniTable*)(table >> 8); +} + +UPB_INLINE +const char* decode_isdonefallback_inl(upb_Decoder* d, const char* ptr, + int overrun, int* status) { + if (overrun < d->limit) { + /* Need to copy remaining data into patch buffer. */ + UPB_ASSERT(overrun < 16); + if (d->unknown_msg) { + if (!_upb_Message_AddUnknown(d->unknown_msg, d->unknown, ptr - d->unknown, + &d->arena)) { + *status = kUpb_DecodeStatus_OutOfMemory; + return NULL; + } + d->unknown = &d->patch[0] + overrun; + } + memset(d->patch + 16, 0, 16); + memcpy(d->patch, d->end, 16); + ptr = &d->patch[0] + overrun; + d->end = &d->patch[16]; + d->limit -= 16; + d->limit_ptr = d->end + d->limit; + d->options &= ~kUpb_DecodeOption_AliasString; + UPB_ASSERT(ptr < d->limit_ptr); + return ptr; + } else { + *status = kUpb_DecodeStatus_Malformed; + return NULL; + } +} + +const char* decode_isdonefallback(upb_Decoder* d, const char* ptr, int overrun); + +UPB_INLINE +bool decode_isdone(upb_Decoder* d, const char** ptr) { + int overrun = *ptr - d->end; + if (UPB_LIKELY(*ptr < d->limit_ptr)) { + return false; + } else if (UPB_LIKELY(overrun == d->limit)) { + return true; + } else { + *ptr = decode_isdonefallback(d, *ptr, overrun); + return false; + } +} + +#if UPB_FASTTABLE +UPB_INLINE +const char* fastdecode_tagdispatch(upb_Decoder* d, const char* ptr, + upb_Message* msg, intptr_t table, + uint64_t hasbits, uint64_t tag) { + const upb_MiniTable* table_p = decode_totablep(table); + uint8_t mask = table; + uint64_t data; + size_t idx = tag & mask; + UPB_ASSUME((idx & 7) == 0); + idx >>= 3; + data = table_p->fasttable[idx].field_data ^ tag; + UPB_MUSTTAIL return table_p->fasttable[idx].field_parser(d, ptr, msg, table, + hasbits, data); +} +#endif + +UPB_INLINE uint32_t fastdecode_loadtag(const char* ptr) { + uint16_t tag; + memcpy(&tag, ptr, 2); + return tag; +} + +UPB_INLINE void decode_checklimit(upb_Decoder* d) { + UPB_ASSERT(d->limit_ptr == d->end + UPB_MIN(0, d->limit)); +} + +UPB_INLINE int decode_pushlimit(upb_Decoder* d, const char* ptr, int size) { + int limit = size + (int)(ptr - d->end); + int delta = d->limit - limit; + decode_checklimit(d); + d->limit = limit; + d->limit_ptr = d->end + UPB_MIN(0, limit); + decode_checklimit(d); + return delta; +} + +UPB_INLINE void decode_poplimit(upb_Decoder* d, const char* ptr, + int saved_delta) { + UPB_ASSERT(ptr - d->end == d->limit); + decode_checklimit(d); + d->limit += saved_delta; + d->limit_ptr = d->end + UPB_MIN(0, d->limit); + decode_checklimit(d); +} + +#include "upb/port_undef.inc" + +#endif /* UPB_INTERNAL_DECODE_H_ */ diff --git a/upb/internal/mini_table_accessors.h b/upb/internal/mini_table_accessors.h new file mode 100644 index 0000000000..23f694baee --- /dev/null +++ b/upb/internal/mini_table_accessors.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009-2022, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_INTERNAL_MINI_TABLE_ACCESSORS_H_ +#define UPB_INTERNAL_MINI_TABLE_ACCESSORS_H_ + +#include "upb/msg_internal.h" + +// Must be last. +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +UPB_INLINE bool _upb_MiniTable_Field_InOneOf(const upb_MiniTable_Field* field) { + return field->presence < 0; +} + +UPB_INLINE void _upb_MiniTable_SetPresence(upb_Message* msg, + const upb_MiniTable_Field* field) { + if (field->presence > 0) { + _upb_sethas_field(msg, field); + } else if (_upb_MiniTable_Field_InOneOf(field)) { + *_upb_oneofcase_field(msg, field) = field->number; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif // UPB_INTERNAL_MINI_TABLE_ACCESSORS_H_ diff --git a/upb/internal/table.h b/upb/internal/table.h new file mode 100644 index 0000000000..bc647be83f --- /dev/null +++ b/upb/internal/table.h @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * upb_table + * + * This header is INTERNAL-ONLY! Its interfaces are not public or stable! + * This file defines very fast int->upb_value (inttable) and string->upb_value + * (strtable) hash tables. + * + * The table uses chained scatter with Brent's variation (inspired by the Lua + * implementation of hash tables). The hash function for strings is Austin + * Appleby's "MurmurHash." + * + * The inttable uses uintptr_t as its key, which guarantees it can be used to + * store pointers or integers of at least 32 bits (upb isn't really useful on + * systems where sizeof(void*) < 4). + * + * The table must be homogeneous (all values of the same type). In debug + * mode, we check this on insert and lookup. + */ + +#ifndef UPB_INTERNAL_TABLE_H_ +#define UPB_INTERNAL_TABLE_H_ + +#include +#include + +#include "upb/upb.h" + +// Must be last. +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +/* upb_value ******************************************************************/ + +typedef struct { + uint64_t val; +} upb_value; + +/* Variant that works with a length-delimited rather than NULL-delimited string, + * as supported by strtable. */ +char* upb_strdup2(const char* s, size_t len, upb_Arena* a); + +UPB_INLINE void _upb_value_setval(upb_value* v, uint64_t val) { v->val = val; } + +/* For each value ctype, define the following set of functions: + * + * // Get/set an int32 from a upb_value. + * int32_t upb_value_getint32(upb_value val); + * void upb_value_setint32(upb_value *val, int32_t cval); + * + * // Construct a new upb_value from an int32. + * upb_value upb_value_int32(int32_t val); */ +#define FUNCS(name, membername, type_t, converter, proto_type) \ + UPB_INLINE void upb_value_set##name(upb_value* val, type_t cval) { \ + val->val = (converter)cval; \ + } \ + UPB_INLINE upb_value upb_value_##name(type_t val) { \ + upb_value ret; \ + upb_value_set##name(&ret, val); \ + return ret; \ + } \ + UPB_INLINE type_t upb_value_get##name(upb_value val) { \ + return (type_t)(converter)val.val; \ + } + +FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32) +FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64) +FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32) +FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64) +FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL) +FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR) +FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR) +FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR) + +#undef FUNCS + +UPB_INLINE void upb_value_setfloat(upb_value* val, float cval) { + memcpy(&val->val, &cval, sizeof(cval)); +} + +UPB_INLINE void upb_value_setdouble(upb_value* val, double cval) { + memcpy(&val->val, &cval, sizeof(cval)); +} + +UPB_INLINE upb_value upb_value_float(float cval) { + upb_value ret; + upb_value_setfloat(&ret, cval); + return ret; +} + +UPB_INLINE upb_value upb_value_double(double cval) { + upb_value ret; + upb_value_setdouble(&ret, cval); + return ret; +} + +#undef SET_TYPE + +/* upb_tabkey *****************************************************************/ + +/* Either: + * 1. an actual integer key, or + * 2. a pointer to a string prefixed by its uint32_t length, owned by us. + * + * ...depending on whether this is a string table or an int table. We would + * make this a union of those two types, but C89 doesn't support statically + * initializing a non-first union member. */ +typedef uintptr_t upb_tabkey; + +UPB_INLINE char* upb_tabstr(upb_tabkey key, uint32_t* len) { + char* mem = (char*)key; + if (len) memcpy(len, mem, sizeof(*len)); + return mem + sizeof(*len); +} + +UPB_INLINE upb_StringView upb_tabstrview(upb_tabkey key) { + upb_StringView ret; + uint32_t len; + ret.data = upb_tabstr(key, &len); + ret.size = len; + return ret; +} + +/* upb_tabval *****************************************************************/ + +typedef struct upb_tabval { + uint64_t val; +} upb_tabval; + +#define UPB_TABVALUE_EMPTY_INIT \ + { -1 } + +/* upb_table ******************************************************************/ + +typedef struct _upb_tabent { + upb_tabkey key; + upb_tabval val; + + /* Internal chaining. This is const so we can create static initializers for + * tables. We cast away const sometimes, but *only* when the containing + * upb_table is known to be non-const. This requires a bit of care, but + * the subtlety is confined to table.c. */ + const struct _upb_tabent* next; +} upb_tabent; + +typedef struct { + size_t count; /* Number of entries in the hash part. */ + uint32_t mask; /* Mask to turn hash value -> bucket. */ + uint32_t max_count; /* Max count before we hit our load limit. */ + uint8_t size_lg2; /* Size of the hashtable part is 2^size_lg2 entries. */ + upb_tabent* entries; +} upb_table; + +typedef struct { + upb_table t; +} upb_strtable; + +typedef struct { + upb_table t; /* For entries that don't fit in the array part. */ + const upb_tabval* array; /* Array part of the table. See const note above. */ + size_t array_size; /* Array part size. */ + size_t array_count; /* Array part number of elements. */ +} upb_inttable; + +UPB_INLINE size_t upb_table_size(const upb_table* t) { + if (t->size_lg2 == 0) + return 0; + else + return 1 << t->size_lg2; +} + +/* Internal-only functions, in .h file only out of necessity. */ +UPB_INLINE bool upb_tabent_isempty(const upb_tabent* e) { return e->key == 0; } + +/* Initialize and uninitialize a table, respectively. If memory allocation + * failed, false is returned that the table is uninitialized. */ +bool upb_inttable_init(upb_inttable* table, upb_Arena* a); +bool upb_strtable_init(upb_strtable* table, size_t expected_size, upb_Arena* a); + +/* Returns the number of values in the table. */ +size_t upb_inttable_count(const upb_inttable* t); +UPB_INLINE size_t upb_strtable_count(const upb_strtable* t) { + return t->t.count; +} + +void upb_strtable_clear(upb_strtable* t); + +/* Inserts the given key into the hashtable with the given value. The key must + * not already exist in the hash table. For strtables, the key is not required + * to be NULL-terminated, and the table will make an internal copy of the key. + * Inttables must not insert a value of UINTPTR_MAX. + * + * If a table resize was required but memory allocation failed, false is + * returned and the table is unchanged. */ +bool upb_inttable_insert(upb_inttable* t, uintptr_t key, upb_value val, + upb_Arena* a); +bool upb_strtable_insert(upb_strtable* t, const char* key, size_t len, + upb_value val, upb_Arena* a); + +/* Looks up key in this table, returning "true" if the key was found. + * If v is non-NULL, copies the value for this key into *v. */ +bool upb_inttable_lookup(const upb_inttable* t, uintptr_t key, upb_value* v); +bool upb_strtable_lookup2(const upb_strtable* t, const char* key, size_t len, + upb_value* v); + +/* For NULL-terminated strings. */ +UPB_INLINE bool upb_strtable_lookup(const upb_strtable* t, const char* key, + upb_value* v) { + return upb_strtable_lookup2(t, key, strlen(key), v); +} + +/* Removes an item from the table. Returns true if the remove was successful, + * and stores the removed item in *val if non-NULL. */ +bool upb_inttable_remove(upb_inttable* t, uintptr_t key, upb_value* val); +bool upb_strtable_remove2(upb_strtable* t, const char* key, size_t len, + upb_value* val); + +UPB_INLINE bool upb_strtable_remove(upb_strtable* t, const char* key, + upb_value* v) { + return upb_strtable_remove2(t, key, strlen(key), v); +} + +/* Updates an existing entry in an inttable. If the entry does not exist, + * returns false and does nothing. Unlike insert/remove, this does not + * invalidate iterators. */ +bool upb_inttable_replace(upb_inttable* t, uintptr_t key, upb_value val); + +/* Optimizes the table for the current set of entries, for both memory use and + * lookup time. Client should call this after all entries have been inserted; + * inserting more entries is legal, but will likely require a table resize. */ +void upb_inttable_compact(upb_inttable* t, upb_Arena* a); + +/* Exposed for testing only. */ +bool upb_strtable_resize(upb_strtable* t, size_t size_lg2, upb_Arena* a); + +/* Iterators ******************************************************************/ + +/* Iteration over inttable. + * + * intptr_t iter = UPB_INTTABLE_BEGIN; + * uintptr_t key; + * upb_value val; + * while (upb_inttable_next2(t, &key, &val, &iter)) { + * // ... + * } + */ + +#define UPB_INTTABLE_BEGIN -1 + +bool upb_inttable_next2(const upb_inttable* t, uintptr_t* key, upb_value* val, + intptr_t* iter); +void upb_inttable_removeiter(upb_inttable* t, intptr_t* iter); + +/* Iteration over strtable. + * + * intptr_t iter = UPB_INTTABLE_BEGIN; + * upb_StringView key; + * upb_value val; + * while (upb_strtable_next2(t, &key, &val, &iter)) { + * // ... + * } + */ + +#define UPB_STRTABLE_BEGIN -1 + +bool upb_strtable_next2(const upb_strtable* t, upb_StringView* key, + upb_value* val, intptr_t* iter); +void upb_strtable_removeiter(upb_strtable* t, intptr_t* iter); + +/* DEPRECATED iterators, slated for removal. + * + * Iterators for int and string tables. We are subject to some kind of unusual + * design constraints: + * + * For high-level languages: + * - we must be able to guarantee that we don't crash or corrupt memory even if + * the program accesses an invalidated iterator. + * + * For C++11 range-based for: + * - iterators must be copyable + * - iterators must be comparable + * - it must be possible to construct an "end" value. + * + * Iteration order is undefined. + * + * Modifying the table invalidates iterators. upb_{str,int}table_done() is + * guaranteed to work even on an invalidated iterator, as long as the table it + * is iterating over has not been freed. Calling next() or accessing data from + * an invalidated iterator yields unspecified elements from the table, but it is + * guaranteed not to crash and to return real table elements (except when done() + * is true). */ + +/* upb_strtable_iter **********************************************************/ + +/* upb_strtable_iter i; + * upb_strtable_begin(&i, t); + * for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { + * const char *key = upb_strtable_iter_key(&i); + * const upb_value val = upb_strtable_iter_value(&i); + * // ... + * } + */ + +typedef struct { + const upb_strtable* t; + size_t index; +} upb_strtable_iter; + +void upb_strtable_begin(upb_strtable_iter* i, const upb_strtable* t); +void upb_strtable_next(upb_strtable_iter* i); +bool upb_strtable_done(const upb_strtable_iter* i); +upb_StringView upb_strtable_iter_key(const upb_strtable_iter* i); +upb_value upb_strtable_iter_value(const upb_strtable_iter* i); +void upb_strtable_iter_setdone(upb_strtable_iter* i); +bool upb_strtable_iter_isequal(const upb_strtable_iter* i1, + const upb_strtable_iter* i2); + +/* upb_inttable_iter **********************************************************/ + +/* upb_inttable_iter i; + * upb_inttable_begin(&i, t); + * for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + * uintptr_t key = upb_inttable_iter_key(&i); + * upb_value val = upb_inttable_iter_value(&i); + * // ... + * } + */ + +typedef struct { + const upb_inttable* t; + size_t index; + bool array_part; +} upb_inttable_iter; + +UPB_INLINE const upb_tabent* str_tabent(const upb_strtable_iter* i) { + return &i->t->t.entries[i->index]; +} + +void upb_inttable_begin(upb_inttable_iter* i, const upb_inttable* t); +void upb_inttable_next(upb_inttable_iter* i); +bool upb_inttable_done(const upb_inttable_iter* i); +uintptr_t upb_inttable_iter_key(const upb_inttable_iter* i); +upb_value upb_inttable_iter_value(const upb_inttable_iter* i); +void upb_inttable_iter_setdone(upb_inttable_iter* i); +bool upb_inttable_iter_isequal(const upb_inttable_iter* i1, + const upb_inttable_iter* i2); + +uint32_t _upb_Hash(const void* p, size_t n, uint64_t seed); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif /* UPB_INTERNAL_TABLE_H_ */ diff --git a/upb/internal/upb.h b/upb/internal/upb.h new file mode 100644 index 0000000000..cdc8bfc228 --- /dev/null +++ b/upb/internal/upb.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_INTERNAL_UPB_H_ +#define UPB_INTERNAL_UPB_H_ + +#include "upb/upb.h" + +struct mem_block; +typedef struct mem_block mem_block; + +struct upb_Arena { + _upb_ArenaHead head; + /* Stores cleanup metadata for this arena. + * - a pointer to the current cleanup counter. + * - a boolean indicating if there is an unowned initial block. */ + uintptr_t cleanup_metadata; + + /* Allocator to allocate arena blocks. We are responsible for freeing these + * when we are destroyed. */ + upb_alloc* block_alloc; + uint32_t last_size; + + /* When multiple arenas are fused together, each arena points to a parent + * arena (root points to itself). The root tracks how many live arenas + * reference it. */ + uint32_t refcount; /* Only used when a->parent == a */ + struct upb_Arena* parent; + + /* Linked list of blocks to free/cleanup. */ + mem_block *freelist, *freelist_tail; +}; + +// Encodes a float or double that is round-trippable, but as short as possible. +// These routines are not fully optimal (not guaranteed to be shortest), but are +// short-ish and match the implementation that has been used in protobuf since +// the beginning. +// +// The given buffer size must be at least kUpb_RoundTripBufferSize. +enum { kUpb_RoundTripBufferSize = 32 }; +void _upb_EncodeRoundTripDouble(double val, char* buf, size_t size); +void _upb_EncodeRoundTripFloat(float val, char* buf, size_t size); + +#endif /* UPB_INTERNAL_UPB_H_ */ diff --git a/upb/json_encode.c b/upb/json_encode.c index 81c0501a4e..e13c0f771d 100644 --- a/upb/json_encode.c +++ b/upb/json_encode.c @@ -37,9 +37,9 @@ #include #include "upb/decode.h" +#include "upb/internal/upb.h" #include "upb/internal/vsnprintf_compat.h" #include "upb/reflection.h" -#include "upb/upb_internal.h" /* Must be last. */ #include "upb/port_def.inc" diff --git a/upb/collections.c b/upb/map.c similarity index 60% rename from upb/collections.c rename to upb/map.c index 6bf7fbbbc0..d92776f546 100644 --- a/upb/collections.c +++ b/upb/map.c @@ -25,13 +25,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "upb/collections.h" +#include "upb/map.h" #include +#include "upb/internal/table.h" #include "upb/msg.h" #include "upb/port_def.inc" -#include "upb/table_internal.h" /* Strings/bytes are special-cased in maps. */ static char _upb_CTypeo_mapsize[12] = { @@ -49,90 +49,6 @@ static char _upb_CTypeo_mapsize[12] = { 0, /* kUpb_CType_Bytes */ }; -static const char _upb_CTypeo_sizelg2[12] = { - 0, - 0, /* kUpb_CType_Bool */ - 2, /* kUpb_CType_Float */ - 2, /* kUpb_CType_Int32 */ - 2, /* kUpb_CType_UInt32 */ - 2, /* kUpb_CType_Enum */ - UPB_SIZE(2, 3), /* kUpb_CType_Message */ - 3, /* kUpb_CType_Double */ - 3, /* kUpb_CType_Int64 */ - 3, /* kUpb_CType_UInt64 */ - UPB_SIZE(3, 4), /* kUpb_CType_String */ - UPB_SIZE(3, 4), /* kUpb_CType_Bytes */ -}; - -/** upb_Array *****************************************************************/ - -upb_Array* upb_Array_New(upb_Arena* a, upb_CType type) { - return _upb_Array_New(a, 4, _upb_CTypeo_sizelg2[type]); -} - -size_t upb_Array_Size(const upb_Array* arr) { return arr->len; } - -upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i) { - upb_MessageValue ret; - const char* data = _upb_array_constptr(arr); - int lg2 = arr->data & 7; - UPB_ASSERT(i < arr->len); - memcpy(&ret, data + (i << lg2), 1 << lg2); - return ret; -} - -void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val) { - char* data = _upb_array_ptr(arr); - int lg2 = arr->data & 7; - UPB_ASSERT(i < arr->len); - memcpy(data + (i << lg2), &val, 1 << lg2); -} - -bool upb_Array_Append(upb_Array* arr, upb_MessageValue val, upb_Arena* arena) { - if (!upb_Array_Resize(arr, arr->len + 1, arena)) { - return false; - } - upb_Array_Set(arr, arr->len - 1, val); - return true; -} - -void upb_Array_Move(upb_Array* arr, size_t dst_idx, size_t src_idx, - size_t count) { - char* data = _upb_array_ptr(arr); - int lg2 = arr->data & 7; - memmove(&data[dst_idx << lg2], &data[src_idx << lg2], count << lg2); -} - -bool upb_Array_Insert(upb_Array* arr, size_t i, size_t count, - upb_Arena* arena) { - UPB_ASSERT(i <= arr->len); - UPB_ASSERT(count + arr->len >= count); - size_t oldsize = arr->len; - if (!upb_Array_Resize(arr, arr->len + count, arena)) { - return false; - } - upb_Array_Move(arr, i + count, i, oldsize - i); - return true; -} - -/* - * i end arr->len - * |------------|XXXXXXXX|--------| - */ -void upb_Array_Delete(upb_Array* arr, size_t i, size_t count) { - size_t end = i + count; - UPB_ASSERT(i <= end); - UPB_ASSERT(end <= arr->len); - upb_Array_Move(arr, i, end, arr->len - end); - arr->len -= count; -} - -bool upb_Array_Resize(upb_Array* arr, size_t size, upb_Arena* arena) { - return _upb_Array_Resize(arr, size, arena); -} - -/** upb_Map *******************************************************************/ - upb_Map* upb_Map_New(upb_Arena* a, upb_CType key_type, upb_CType value_type) { return _upb_Map_New(a, _upb_CTypeo_mapsize[key_type], _upb_CTypeo_mapsize[value_type]); diff --git a/upb/map.h b/upb/map.h new file mode 100644 index 0000000000..8ee0ddd05e --- /dev/null +++ b/upb/map.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_MAP_H_ +#define UPB_MAP_H_ + +#include "google/protobuf/descriptor.upb.h" +#include "upb/message_value.h" + +// Must be last. +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Creates a new map on the given arena with the given key/value size. */ +upb_Map* upb_Map_New(upb_Arena* a, upb_CType key_type, upb_CType value_type); + +/* Returns the number of entries in the map. */ +size_t upb_Map_Size(const upb_Map* map); + +/* Stores a value for the given key into |*val| (or the zero value if the key is + * not present). Returns whether the key was present. The |val| pointer may be + * NULL, in which case the function tests whether the given key is present. */ +bool upb_Map_Get(const upb_Map* map, upb_MessageValue key, + upb_MessageValue* val); + +/* Removes all entries in the map. */ +void upb_Map_Clear(upb_Map* map); + +typedef enum { + // LINT.IfChange + kUpb_MapInsertStatus_Inserted = 0, + kUpb_MapInsertStatus_Replaced = 1, + kUpb_MapInsertStatus_OutOfMemory = 2, + // LINT.ThenChange(//depot/google3/third_party/upb/upb/msg_internal.h) +} upb_MapInsertStatus; + +/* Sets the given key to the given value, returning whether the key was inserted + * or replaced. If the key was inserted, then any existing iterators will be + * invalidated. */ +upb_MapInsertStatus upb_Map_Insert(upb_Map* map, upb_MessageValue key, + upb_MessageValue val, upb_Arena* arena); + +/* Sets the given key to the given value. Returns false if memory allocation + * failed. If the key is newly inserted, then any existing iterators will be + * invalidated. */ +UPB_INLINE bool upb_Map_Set(upb_Map* map, upb_MessageValue key, + upb_MessageValue val, upb_Arena* arena) { + return upb_Map_Insert(map, key, val, arena) != + kUpb_MapInsertStatus_OutOfMemory; +} + +/* Deletes this key from the table. Returns true if the key was present. */ +bool upb_Map_Delete(upb_Map* map, upb_MessageValue key); + +/* Map iteration: + * + * size_t iter = kUpb_Map_Begin; + * while (upb_MapIterator_Next(map, &iter)) { + * upb_MessageValue key = upb_MapIterator_Key(map, iter); + * upb_MessageValue val = upb_MapIterator_Value(map, iter); + * + * // If mutating is desired. + * upb_MapIterator_SetValue(map, iter, value2); + * } + */ + +/* Advances to the next entry. Returns false if no more entries are present. */ +bool upb_MapIterator_Next(const upb_Map* map, size_t* iter); + +/* Returns true if the iterator still points to a valid entry, or false if the + * iterator is past the last element. It is an error to call this function with + * kUpb_Map_Begin (you must call next() at least once first). */ +bool upb_MapIterator_Done(const upb_Map* map, size_t iter); + +/* Returns the key and value for this entry of the map. */ +upb_MessageValue upb_MapIterator_Key(const upb_Map* map, size_t iter); +upb_MessageValue upb_MapIterator_Value(const upb_Map* map, size_t iter); + +/* Sets the value for this entry. The iterator must not be done, and the + * iterator must not have been initialized const. */ +void upb_MapIterator_SetValue(upb_Map* map, size_t iter, + upb_MessageValue value); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif /* UPB_MAP_H_ */ diff --git a/upb/message_value.h b/upb/message_value.h new file mode 100644 index 0000000000..a08b90fc1e --- /dev/null +++ b/upb/message_value.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_MESSAGE_VALUE_H_ +#define UPB_MESSAGE_VALUE_H_ + +#include "google/protobuf/descriptor.upb.h" +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +// Definitions commn to both upb_Array and upb_Map. + +typedef union { + bool bool_val; + float float_val; + double double_val; + int32_t int32_val; + int64_t int64_val; + uint32_t uint32_val; + uint64_t uint64_val; + const upb_Map* map_val; + const upb_Message* msg_val; + const upb_Array* array_val; + upb_StringView str_val; +} upb_MessageValue; + +typedef union { + upb_Map* map; + upb_Message* msg; + upb_Array* array; +} upb_MutableMessageValue; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif /* UPB_MESSAGE_VALUE_H_ */ diff --git a/upb/mini_table_accessors.h b/upb/mini_table_accessors.h index e80b20e941..750f66f207 100644 --- a/upb/mini_table_accessors.h +++ b/upb/mini_table_accessors.h @@ -28,8 +28,8 @@ #ifndef UPB_MINI_TABLE_ACCESSORS_H_ #define UPB_MINI_TABLE_ACCESSORS_H_ -#include "upb/collections.h" -#include "upb/mini_table_accessors_internal.h" +#include "upb/array.h" +#include "upb/internal/mini_table_accessors.h" #include "upb/msg_internal.h" // Must be last. diff --git a/upb/mini_table_accessors_internal.h b/upb/mini_table_accessors_internal.h index a2cfa44df8..8368b65bd4 100644 --- a/upb/mini_table_accessors_internal.h +++ b/upb/mini_table_accessors_internal.h @@ -28,32 +28,9 @@ #ifndef UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_ #define UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_ -#include "upb/msg_internal.h" +// TODO(b/232091617): Delete this entire header which currently exists only for +// temporary backwards compatibility. -// Must be last. -#include "upb/port_def.inc" - -#ifdef __cplusplus -extern "C" { -#endif - -UPB_INLINE bool _upb_MiniTable_Field_InOneOf(const upb_MiniTable_Field* field) { - return field->presence < 0; -} - -UPB_INLINE void _upb_MiniTable_SetPresence(upb_Message* msg, - const upb_MiniTable_Field* field) { - if (field->presence > 0) { - _upb_sethas_field(msg, field); - } else if (_upb_MiniTable_Field_InOneOf(field)) { - *_upb_oneofcase_field(msg, field) = field->number; - } -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#include "upb/port_undef.inc" +#include "upb/internal/mini_table_accessors.h" #endif // UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_ diff --git a/upb/mini_table_accessors_test.cc b/upb/mini_table_accessors_test.cc index 7758e19970..16274476ad 100644 --- a/upb/mini_table_accessors_test.cc +++ b/upb/mini_table_accessors_test.cc @@ -36,7 +36,7 @@ #include "gtest/gtest.h" #include "google/protobuf/test_messages_proto2.upb.h" #include "google/protobuf/test_messages_proto3.upb.h" -#include "upb/collections.h" +#include "upb/array.h" #include "upb/mini_table.h" #include "upb/test.upb.h" diff --git a/upb/msg.c b/upb/msg.c index 46738ca1fb..036c62199d 100644 --- a/upb/msg.c +++ b/upb/msg.c @@ -27,9 +27,9 @@ #include "upb/msg.h" +#include "upb/internal/table.h" #include "upb/msg_internal.h" #include "upb/port_def.inc" -#include "upb/table_internal.h" /** upb_Message ***************************************************************/ @@ -366,63 +366,3 @@ bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type, qsort(&s->entries[sorted->start], map_size, sizeof(*s->entries), compar); return true; } - -/** upb_ExtensionRegistry *****************************************************/ - -struct upb_ExtensionRegistry { - upb_Arena* arena; - upb_strtable exts; /* Key is upb_MiniTable* concatenated with fieldnum. */ -}; - -#define EXTREG_KEY_SIZE (sizeof(upb_MiniTable*) + sizeof(uint32_t)) - -static void extreg_key(char* buf, const upb_MiniTable* l, uint32_t fieldnum) { - memcpy(buf, &l, sizeof(l)); - memcpy(buf + sizeof(l), &fieldnum, sizeof(fieldnum)); -} - -upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena) { - upb_ExtensionRegistry* r = upb_Arena_Malloc(arena, sizeof(*r)); - if (!r) return NULL; - r->arena = arena; - if (!upb_strtable_init(&r->exts, 8, arena)) return NULL; - return r; -} - -bool _upb_extreg_add(upb_ExtensionRegistry* r, - const upb_MiniTable_Extension** e, size_t count) { - char buf[EXTREG_KEY_SIZE]; - const upb_MiniTable_Extension** start = e; - const upb_MiniTable_Extension** end = UPB_PTRADD(e, count); - for (; e < end; e++) { - const upb_MiniTable_Extension* ext = *e; - extreg_key(buf, ext->extendee, ext->field.number); - if (!upb_strtable_insert(&r->exts, buf, EXTREG_KEY_SIZE, - upb_value_constptr(ext), r->arena)) { - goto failure; - } - } - return true; - -failure: - /* Back out the entries previously added. */ - for (end = e, e = start; e < end; e++) { - const upb_MiniTable_Extension* ext = *e; - extreg_key(buf, ext->extendee, ext->field.number); - upb_strtable_remove2(&r->exts, buf, EXTREG_KEY_SIZE, NULL); - } - return false; -} - -const upb_MiniTable_Extension* _upb_extreg_get(const upb_ExtensionRegistry* r, - const upb_MiniTable* l, - uint32_t num) { - char buf[EXTREG_KEY_SIZE]; - upb_value v; - extreg_key(buf, l, num); - if (upb_strtable_lookup2(&r->exts, buf, EXTREG_KEY_SIZE, &v)) { - return upb_value_getconstptr(v); - } else { - return NULL; - } -} diff --git a/upb/msg.h b/upb/msg.h index c984b137b1..5c1e8e8931 100644 --- a/upb/msg.h +++ b/upb/msg.h @@ -38,14 +38,14 @@ #include +// TODO(b/232091617): Remove this and fix everything that breaks as a result. +#include "upb/extension_registry.h" #include "upb/upb.h" #ifdef __cplusplus extern "C" { #endif -/** upb_Message ***************************************************************/ - typedef void upb_Message; /* For users these are opaque. They can be obtained from @@ -64,49 +64,6 @@ const char* upb_Message_GetUnknown(const upb_Message* msg, size_t* len); /* Returns the number of extensions present in this message. */ size_t upb_Message_ExtensionCount(const upb_Message* msg); -/** upb_ExtensionRegistry *****************************************************/ - -/* Extension registry: a dynamic data structure that stores a map of: - * (upb_MiniTable, number) -> extension info - * - * upb_decode() uses upb_ExtensionRegistry to look up extensions while parsing - * binary format. - * - * upb_ExtensionRegistry is part of the mini-table (msglayout) family of - * objects. Like all mini-table objects, it is suitable for reflection-less - * builds that do not want to expose names into the binary. - * - * Unlike most mini-table types, upb_ExtensionRegistry requires dynamic memory - * allocation and dynamic initialization: - * * If reflection is being used, then upb_DefPool will construct an appropriate - * upb_ExtensionRegistry automatically. - * * For a mini-table only build, the user must manually construct the - * upb_ExtensionRegistry and populate it with all of the extensions the user - * cares about. - * * A third alternative is to manually unpack relevant extensions after the - * main parse is complete, similar to how Any works. This is perhaps the - * nicest solution from the perspective of reducing dependencies, avoiding - * dynamic memory allocation, and avoiding the need to parse uninteresting - * extensions. The downsides are: - * (1) parse errors are not caught during the main parse - * (2) the CPU hit of parsing comes during access, which could cause an - * undesirable stutter in application performance. - * - * Users cannot directly get or put into this map. Users can only add the - * extensions from a generated module and pass the extension registry to the - * binary decoder. - * - * A upb_DefPool provides a upb_ExtensionRegistry, so any users who use - * reflection do not need to populate a upb_ExtensionRegistry directly. - */ - -struct upb_ExtensionRegistry; -typedef struct upb_ExtensionRegistry upb_ExtensionRegistry; - -/* Creates a upb_ExtensionRegistry in the given arena. The arena must outlive - * any use of the extreg. */ -upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/upb/msg_internal.h b/upb/msg_internal.h index 62ef185873..4f5c4797c5 100644 --- a/upb/msg_internal.h +++ b/upb/msg_internal.h @@ -39,8 +39,9 @@ #include #include +#include "upb/extension_registry.h" +#include "upb/internal/table.h" #include "upb/msg.h" -#include "upb/table_internal.h" #include "upb/upb.h" /* Must be last. */ @@ -672,7 +673,7 @@ typedef enum { _kUpb_MapInsertStatus_Inserted = 0, _kUpb_MapInsertStatus_Replaced = 1, _kUpb_MapInsertStatus_OutOfMemory = 2, - // LINT.ThenChange(//depot/google3/third_party/upb/upb/collections.h) + // LINT.ThenChange(//depot/google3/third_party/upb/upb/map.h) } _upb_MapInsertStatus; UPB_INLINE _upb_MapInsertStatus _upb_Map_Insert(upb_Map* map, const void* key, diff --git a/upb/reflection.c b/upb/reflection.c index 0c92248971..43f884495f 100644 --- a/upb/reflection.c +++ b/upb/reflection.c @@ -29,9 +29,9 @@ #include +#include "upb/internal/table.h" #include "upb/msg.h" #include "upb/port_def.inc" -#include "upb/table_internal.h" static size_t get_field_size(const upb_MiniTable_Field* f) { static unsigned char sizes[] = { diff --git a/upb/reflection.h b/upb/reflection.h index acae9ab057..6071fba6f5 100644 --- a/upb/reflection.h +++ b/upb/reflection.h @@ -28,8 +28,9 @@ #ifndef UPB_REFLECTION_H_ #define UPB_REFLECTION_H_ -#include "upb/collections.h" +#include "upb/array.h" #include "upb/def.h" +#include "upb/map.h" #include "upb/msg.h" #include "upb/port_def.inc" #include "upb/upb.h" diff --git a/upb/status.c b/upb/status.c new file mode 100644 index 0000000000..c00b9c86b2 --- /dev/null +++ b/upb/status.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/status.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "upb/internal/upb.h" + +// Must be last. +#include "upb/port_def.inc" + +void upb_Status_Clear(upb_Status* status) { + if (!status) return; + status->ok = true; + status->msg[0] = '\0'; +} + +bool upb_Status_IsOk(const upb_Status* status) { return status->ok; } + +const char* upb_Status_ErrorMessage(const upb_Status* status) { + return status->msg; +} + +void upb_Status_SetErrorMessage(upb_Status* status, const char* msg) { + if (!status) return; + status->ok = false; + strncpy(status->msg, msg, _kUpb_Status_MaxMessage - 1); + status->msg[_kUpb_Status_MaxMessage - 1] = '\0'; +} + +void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + upb_Status_VSetErrorFormat(status, fmt, args); + va_end(args); +} + +void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt, + va_list args) { + if (!status) return; + status->ok = false; + vsnprintf(status->msg, sizeof(status->msg), fmt, args); + status->msg[_kUpb_Status_MaxMessage - 1] = '\0'; +} + +void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt, + va_list args) { + size_t len; + if (!status) return; + status->ok = false; + len = strlen(status->msg); + vsnprintf(status->msg + len, sizeof(status->msg) - len, fmt, args); + status->msg[_kUpb_Status_MaxMessage - 1] = '\0'; +} diff --git a/upb/status.h b/upb/status.h new file mode 100644 index 0000000000..2c5c2a7f8b --- /dev/null +++ b/upb/status.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_STATUS_H_ +#define UPB_STATUS_H_ + +#include +#include + +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +#define _kUpb_Status_MaxMessage 127 + +typedef struct { + bool ok; + char msg[_kUpb_Status_MaxMessage]; /* Error message; NULL-terminated. */ +} upb_Status; + +const char* upb_Status_ErrorMessage(const upb_Status* status); +bool upb_Status_IsOk(const upb_Status* status); + +/* These are no-op if |status| is NULL. */ +void upb_Status_Clear(upb_Status* status); +void upb_Status_SetErrorMessage(upb_Status* status, const char* msg); +void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...) + UPB_PRINTF(2, 3); +void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt, + va_list args) UPB_PRINTF(2, 0); +void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt, + va_list args) UPB_PRINTF(2, 0); + +#include "upb/port_undef.inc" + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* UPB_STATUS_H_ */ diff --git a/upb/table.c b/upb/table.c index 941f258411..e8e55f7e34 100644 --- a/upb/table.c +++ b/upb/table.c @@ -31,9 +31,9 @@ * Implementation is heavily inspired by Lua's ltable.c. */ -#include +#include "upb/internal/table.h" -#include "upb/table_internal.h" +#include /* Must be last. */ #include "upb/port_def.inc" diff --git a/upb/table_internal.h b/upb/table_internal.h index 1e774708d3..7ac8bf78f9 100644 --- a/upb/table_internal.h +++ b/upb/table_internal.h @@ -25,361 +25,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - * upb_table - * - * This header is INTERNAL-ONLY! Its interfaces are not public or stable! - * This file defines very fast int->upb_value (inttable) and string->upb_value - * (strtable) hash tables. - * - * The table uses chained scatter with Brent's variation (inspired by the Lua - * implementation of hash tables). The hash function for strings is Austin - * Appleby's "MurmurHash." - * - * The inttable uses uintptr_t as its key, which guarantees it can be used to - * store pointers or integers of at least 32 bits (upb isn't really useful on - * systems where sizeof(void*) < 4). - * - * The table must be homogeneous (all values of the same type). In debug - * mode, we check this on insert and lookup. - */ - #ifndef UPB_TABLE_H_ #define UPB_TABLE_H_ -#include -#include - -#include "upb/upb.h" - -// Must be last. -#include "upb/port_def.inc" - -#ifdef __cplusplus -extern "C" { -#endif - -/* upb_value ******************************************************************/ - -typedef struct { - uint64_t val; -} upb_value; - -/* Variant that works with a length-delimited rather than NULL-delimited string, - * as supported by strtable. */ -char* upb_strdup2(const char* s, size_t len, upb_Arena* a); - -UPB_INLINE void _upb_value_setval(upb_value* v, uint64_t val) { v->val = val; } - -/* For each value ctype, define the following set of functions: - * - * // Get/set an int32 from a upb_value. - * int32_t upb_value_getint32(upb_value val); - * void upb_value_setint32(upb_value *val, int32_t cval); - * - * // Construct a new upb_value from an int32. - * upb_value upb_value_int32(int32_t val); */ -#define FUNCS(name, membername, type_t, converter, proto_type) \ - UPB_INLINE void upb_value_set##name(upb_value* val, type_t cval) { \ - val->val = (converter)cval; \ - } \ - UPB_INLINE upb_value upb_value_##name(type_t val) { \ - upb_value ret; \ - upb_value_set##name(&ret, val); \ - return ret; \ - } \ - UPB_INLINE type_t upb_value_get##name(upb_value val) { \ - return (type_t)(converter)val.val; \ - } - -FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32) -FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64) -FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32) -FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64) -FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL) -FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR) -FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR) -FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR) - -#undef FUNCS - -UPB_INLINE void upb_value_setfloat(upb_value* val, float cval) { - memcpy(&val->val, &cval, sizeof(cval)); -} - -UPB_INLINE void upb_value_setdouble(upb_value* val, double cval) { - memcpy(&val->val, &cval, sizeof(cval)); -} - -UPB_INLINE upb_value upb_value_float(float cval) { - upb_value ret; - upb_value_setfloat(&ret, cval); - return ret; -} - -UPB_INLINE upb_value upb_value_double(double cval) { - upb_value ret; - upb_value_setdouble(&ret, cval); - return ret; -} - -#undef SET_TYPE - -/* upb_tabkey *****************************************************************/ - -/* Either: - * 1. an actual integer key, or - * 2. a pointer to a string prefixed by its uint32_t length, owned by us. - * - * ...depending on whether this is a string table or an int table. We would - * make this a union of those two types, but C89 doesn't support statically - * initializing a non-first union member. */ -typedef uintptr_t upb_tabkey; - -UPB_INLINE char* upb_tabstr(upb_tabkey key, uint32_t* len) { - char* mem = (char*)key; - if (len) memcpy(len, mem, sizeof(*len)); - return mem + sizeof(*len); -} - -UPB_INLINE upb_StringView upb_tabstrview(upb_tabkey key) { - upb_StringView ret; - uint32_t len; - ret.data = upb_tabstr(key, &len); - ret.size = len; - return ret; -} - -/* upb_tabval *****************************************************************/ - -typedef struct upb_tabval { - uint64_t val; -} upb_tabval; - -#define UPB_TABVALUE_EMPTY_INIT \ - { -1 } - -/* upb_table ******************************************************************/ - -typedef struct _upb_tabent { - upb_tabkey key; - upb_tabval val; - - /* Internal chaining. This is const so we can create static initializers for - * tables. We cast away const sometimes, but *only* when the containing - * upb_table is known to be non-const. This requires a bit of care, but - * the subtlety is confined to table.c. */ - const struct _upb_tabent* next; -} upb_tabent; - -typedef struct { - size_t count; /* Number of entries in the hash part. */ - uint32_t mask; /* Mask to turn hash value -> bucket. */ - uint32_t max_count; /* Max count before we hit our load limit. */ - uint8_t size_lg2; /* Size of the hashtable part is 2^size_lg2 entries. */ - upb_tabent* entries; -} upb_table; - -typedef struct { - upb_table t; -} upb_strtable; - -typedef struct { - upb_table t; /* For entries that don't fit in the array part. */ - const upb_tabval* array; /* Array part of the table. See const note above. */ - size_t array_size; /* Array part size. */ - size_t array_count; /* Array part number of elements. */ -} upb_inttable; - -UPB_INLINE size_t upb_table_size(const upb_table* t) { - if (t->size_lg2 == 0) - return 0; - else - return 1 << t->size_lg2; -} - -/* Internal-only functions, in .h file only out of necessity. */ -UPB_INLINE bool upb_tabent_isempty(const upb_tabent* e) { return e->key == 0; } - -/* Initialize and uninitialize a table, respectively. If memory allocation - * failed, false is returned that the table is uninitialized. */ -bool upb_inttable_init(upb_inttable* table, upb_Arena* a); -bool upb_strtable_init(upb_strtable* table, size_t expected_size, upb_Arena* a); - -/* Returns the number of values in the table. */ -size_t upb_inttable_count(const upb_inttable* t); -UPB_INLINE size_t upb_strtable_count(const upb_strtable* t) { - return t->t.count; -} - -void upb_strtable_clear(upb_strtable* t); - -/* Inserts the given key into the hashtable with the given value. The key must - * not already exist in the hash table. For strtables, the key is not required - * to be NULL-terminated, and the table will make an internal copy of the key. - * Inttables must not insert a value of UINTPTR_MAX. - * - * If a table resize was required but memory allocation failed, false is - * returned and the table is unchanged. */ -bool upb_inttable_insert(upb_inttable* t, uintptr_t key, upb_value val, - upb_Arena* a); -bool upb_strtable_insert(upb_strtable* t, const char* key, size_t len, - upb_value val, upb_Arena* a); - -/* Looks up key in this table, returning "true" if the key was found. - * If v is non-NULL, copies the value for this key into *v. */ -bool upb_inttable_lookup(const upb_inttable* t, uintptr_t key, upb_value* v); -bool upb_strtable_lookup2(const upb_strtable* t, const char* key, size_t len, - upb_value* v); - -/* For NULL-terminated strings. */ -UPB_INLINE bool upb_strtable_lookup(const upb_strtable* t, const char* key, - upb_value* v) { - return upb_strtable_lookup2(t, key, strlen(key), v); -} - -/* Removes an item from the table. Returns true if the remove was successful, - * and stores the removed item in *val if non-NULL. */ -bool upb_inttable_remove(upb_inttable* t, uintptr_t key, upb_value* val); -bool upb_strtable_remove2(upb_strtable* t, const char* key, size_t len, - upb_value* val); - -UPB_INLINE bool upb_strtable_remove(upb_strtable* t, const char* key, - upb_value* v) { - return upb_strtable_remove2(t, key, strlen(key), v); -} - -/* Updates an existing entry in an inttable. If the entry does not exist, - * returns false and does nothing. Unlike insert/remove, this does not - * invalidate iterators. */ -bool upb_inttable_replace(upb_inttable* t, uintptr_t key, upb_value val); - -/* Optimizes the table for the current set of entries, for both memory use and - * lookup time. Client should call this after all entries have been inserted; - * inserting more entries is legal, but will likely require a table resize. */ -void upb_inttable_compact(upb_inttable* t, upb_Arena* a); - -/* Exposed for testing only. */ -bool upb_strtable_resize(upb_strtable* t, size_t size_lg2, upb_Arena* a); - -/* Iterators ******************************************************************/ - -/* Iteration over inttable. - * - * intptr_t iter = UPB_INTTABLE_BEGIN; - * uintptr_t key; - * upb_value val; - * while (upb_inttable_next2(t, &key, &val, &iter)) { - * // ... - * } - */ - -#define UPB_INTTABLE_BEGIN -1 - -bool upb_inttable_next2(const upb_inttable* t, uintptr_t* key, upb_value* val, - intptr_t* iter); -void upb_inttable_removeiter(upb_inttable* t, intptr_t* iter); - -/* Iteration over strtable. - * - * intptr_t iter = UPB_INTTABLE_BEGIN; - * upb_StringView key; - * upb_value val; - * while (upb_strtable_next2(t, &key, &val, &iter)) { - * // ... - * } - */ - -#define UPB_STRTABLE_BEGIN -1 - -bool upb_strtable_next2(const upb_strtable* t, upb_StringView* key, - upb_value* val, intptr_t* iter); -void upb_strtable_removeiter(upb_strtable* t, intptr_t* iter); - -/* DEPRECATED iterators, slated for removal. - * - * Iterators for int and string tables. We are subject to some kind of unusual - * design constraints: - * - * For high-level languages: - * - we must be able to guarantee that we don't crash or corrupt memory even if - * the program accesses an invalidated iterator. - * - * For C++11 range-based for: - * - iterators must be copyable - * - iterators must be comparable - * - it must be possible to construct an "end" value. - * - * Iteration order is undefined. - * - * Modifying the table invalidates iterators. upb_{str,int}table_done() is - * guaranteed to work even on an invalidated iterator, as long as the table it - * is iterating over has not been freed. Calling next() or accessing data from - * an invalidated iterator yields unspecified elements from the table, but it is - * guaranteed not to crash and to return real table elements (except when done() - * is true). */ - -/* upb_strtable_iter **********************************************************/ - -/* upb_strtable_iter i; - * upb_strtable_begin(&i, t); - * for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { - * const char *key = upb_strtable_iter_key(&i); - * const upb_value val = upb_strtable_iter_value(&i); - * // ... - * } - */ - -typedef struct { - const upb_strtable* t; - size_t index; -} upb_strtable_iter; - -void upb_strtable_begin(upb_strtable_iter* i, const upb_strtable* t); -void upb_strtable_next(upb_strtable_iter* i); -bool upb_strtable_done(const upb_strtable_iter* i); -upb_StringView upb_strtable_iter_key(const upb_strtable_iter* i); -upb_value upb_strtable_iter_value(const upb_strtable_iter* i); -void upb_strtable_iter_setdone(upb_strtable_iter* i); -bool upb_strtable_iter_isequal(const upb_strtable_iter* i1, - const upb_strtable_iter* i2); - -/* upb_inttable_iter **********************************************************/ - -/* upb_inttable_iter i; - * upb_inttable_begin(&i, t); - * for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - * uintptr_t key = upb_inttable_iter_key(&i); - * upb_value val = upb_inttable_iter_value(&i); - * // ... - * } - */ - -typedef struct { - const upb_inttable* t; - size_t index; - bool array_part; -} upb_inttable_iter; - -UPB_INLINE const upb_tabent* str_tabent(const upb_strtable_iter* i) { - return &i->t->t.entries[i->index]; -} - -void upb_inttable_begin(upb_inttable_iter* i, const upb_inttable* t); -void upb_inttable_next(upb_inttable_iter* i); -bool upb_inttable_done(const upb_inttable_iter* i); -uintptr_t upb_inttable_iter_key(const upb_inttable_iter* i); -upb_value upb_inttable_iter_value(const upb_inttable_iter* i); -void upb_inttable_iter_setdone(upb_inttable_iter* i); -bool upb_inttable_iter_isequal(const upb_inttable_iter* i1, - const upb_inttable_iter* i2); - -uint32_t _upb_Hash(const void* p, size_t n, uint64_t seed); - -#ifdef __cplusplus -} /* extern "C" */ -#endif +// TODO(b/232091617): Delete this entire header which currently exists only for +// temporary backwards compatibility. -#include "upb/port_undef.inc" +#include "upb/internal/table.h" #endif /* UPB_TABLE_H_ */ diff --git a/upb/test_table.cc b/upb/test_table.cc index 2c368f839c..ddb129a38e 100644 --- a/upb/test_table.cc +++ b/upb/test_table.cc @@ -39,7 +39,7 @@ #include #include "gtest/gtest.h" -#include "upb/table_internal.h" +#include "upb/internal/table.h" #include "upb/upb.hpp" // Must be last. diff --git a/upb/text_encode.c b/upb/text_encode.c index 4f5710b984..dfb213f2bb 100644 --- a/upb/text_encode.c +++ b/upb/text_encode.c @@ -34,9 +34,9 @@ #include #include +#include "upb/internal/upb.h" #include "upb/internal/vsnprintf_compat.h" #include "upb/reflection.h" -#include "upb/upb_internal.h" // Must be last. #include "upb/port_def.inc" diff --git a/upb/upb.c b/upb/upb.c index 5bf74bfcbc..08ae964777 100644 --- a/upb/upb.c +++ b/upb/upb.c @@ -25,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "upb/internal/upb.h" + #include #include #include @@ -34,299 +36,12 @@ #include #include -#include "upb/upb_internal.h" +#include "upb/arena.h" +#include "upb/status.h" // Must be last. #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_Status_IsOk(const upb_Status* status) { return status->ok; } - -const char* upb_Status_ErrorMessage(const upb_Status* status) { - return status->msg; -} - -void upb_Status_SetErrorMessage(upb_Status* status, const char* msg) { - if (!status) return; - status->ok = false; - strncpy(status->msg, msg, _kUpb_Status_MaxMessage - 1); - status->msg[_kUpb_Status_MaxMessage - 1] = '\0'; -} - -void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - upb_Status_VSetErrorFormat(status, fmt, args); - va_end(args); -} - -void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt, - va_list args) { - if (!status) return; - status->ok = false; - vsnprintf(status->msg, sizeof(status->msg), fmt, args); - status->msg[_kUpb_Status_MaxMessage - 1] = '\0'; -} - -void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt, - va_list args) { - size_t len; - if (!status) return; - status->ok = false; - len = strlen(status->msg); - vsnprintf(status->msg + len, sizeof(status->msg) - len, fmt, args); - status->msg[_kUpb_Status_MaxMessage - 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); - } -} - -static uint32_t* upb_cleanup_pointer(uintptr_t cleanup_metadata) { - return (uint32_t*)(cleanup_metadata & ~0x1); -} - -static bool upb_cleanup_has_initial_block(uintptr_t cleanup_metadata) { - return cleanup_metadata & 0x1; -} - -static uintptr_t upb_cleanup_metadata(uint32_t* cleanup, - bool has_initial_block) { - return (uintptr_t)cleanup | has_initial_block; -} - -upb_alloc upb_alloc_global = {&upb_global_allocfunc}; - -/* upb_Arena ******************************************************************/ - -struct mem_block { - struct mem_block* next; - uint32_t size; - uint32_t cleanups; - /* Data follows. */ -}; - -typedef struct cleanup_ent { - upb_CleanupFunc* cleanup; - void* ud; -} cleanup_ent; - -static const size_t memblock_reserve = - UPB_ALIGN_UP(sizeof(mem_block), UPB_MALLOC_ALIGN); - -static upb_Arena* arena_findroot(upb_Arena* a) { - /* Path splitting keeps time complexity down, see: - * https://en.wikipedia.org/wiki/Disjoint-set_data_structure */ - while (a->parent != a) { - upb_Arena* next = a->parent; - a->parent = next->parent; - a = next; - } - return a; -} - -static void upb_Arena_addblock(upb_Arena* a, upb_Arena* root, void* ptr, - size_t size) { - mem_block* block = ptr; - - /* The block is for arena |a|, but should appear in the freelist of |root|. */ - block->next = root->freelist; - block->size = (uint32_t)size; - block->cleanups = 0; - root->freelist = block; - a->last_size = block->size; - if (!root->freelist_tail) root->freelist_tail = block; - - a->head.ptr = UPB_PTR_AT(block, memblock_reserve, char); - a->head.end = UPB_PTR_AT(block, size, char); - a->cleanup_metadata = upb_cleanup_metadata( - &block->cleanups, upb_cleanup_has_initial_block(a->cleanup_metadata)); - - UPB_POISON_MEMORY_REGION(a->head.ptr, a->head.end - a->head.ptr); -} - -static bool upb_Arena_Allocblock(upb_Arena* a, size_t size) { - upb_Arena* root = arena_findroot(a); - size_t block_size = UPB_MAX(size, a->last_size * 2) + memblock_reserve; - mem_block* block = upb_malloc(root->block_alloc, block_size); - - if (!block) return false; - upb_Arena_addblock(a, root, block, block_size); - return true; -} - -void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size) { - if (!upb_Arena_Allocblock(a, size)) return NULL; /* Out of memory. */ - UPB_ASSERT(_upb_ArenaHas(a) >= size); - 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. */ - return upb_Arena_Realloc(a, ptr, oldsize, size); -} - -/* Public Arena API ***********************************************************/ - -upb_Arena* arena_initslow(void* mem, size_t n, upb_alloc* alloc) { - const size_t first_block_overhead = sizeof(upb_Arena) + memblock_reserve; - upb_Arena* a; - - /* We need to malloc the initial block. */ - n = first_block_overhead + 256; - if (!alloc || !(mem = upb_malloc(alloc, n))) { - return NULL; - } - - a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena); - n -= sizeof(*a); - - a->head.alloc.func = &upb_Arena_doalloc; - a->block_alloc = alloc; - a->parent = a; - a->refcount = 1; - a->freelist = NULL; - a->freelist_tail = NULL; - a->cleanup_metadata = upb_cleanup_metadata(NULL, false); - - upb_Arena_addblock(a, a, mem, n); - - return a; -} - -upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc) { - upb_Arena* a; - - if (n) { - /* Align initial pointer up so that we return properly-aligned pointers. */ - void* aligned = (void*)UPB_ALIGN_UP((uintptr_t)mem, UPB_MALLOC_ALIGN); - size_t delta = (uintptr_t)aligned - (uintptr_t)mem; - n = delta <= n ? n - delta : 0; - mem = aligned; - } - - /* Round block size down to alignof(*a) since we will allocate the arena - * itself at the end. */ - n = UPB_ALIGN_DOWN(n, UPB_ALIGN_OF(upb_Arena)); - - if (UPB_UNLIKELY(n < sizeof(upb_Arena))) { - return arena_initslow(mem, n, alloc); - } - - a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena); - - a->head.alloc.func = &upb_Arena_doalloc; - a->block_alloc = alloc; - a->parent = a; - a->refcount = 1; - a->last_size = UPB_MAX(128, n); - a->head.ptr = mem; - a->head.end = UPB_PTR_AT(mem, n - sizeof(*a), char); - a->freelist = NULL; - a->cleanup_metadata = upb_cleanup_metadata(NULL, true); - - return a; -} - -static void arena_dofree(upb_Arena* a) { - mem_block* block = a->freelist; - UPB_ASSERT(a->parent == a); - UPB_ASSERT(a->refcount == 0); - - while (block) { - /* Load first since we are deleting block. */ - mem_block* next = block->next; - - if (block->cleanups > 0) { - cleanup_ent* end = UPB_PTR_AT(block, block->size, void); - cleanup_ent* ptr = end - block->cleanups; - - for (; ptr < end; ptr++) { - ptr->cleanup(ptr->ud); - } - } - - upb_free(a->block_alloc, block); - block = next; - } -} - -void upb_Arena_Free(upb_Arena* a) { - a = arena_findroot(a); - if (--a->refcount == 0) arena_dofree(a); -} - -bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func) { - cleanup_ent* ent; - uint32_t* cleanups = upb_cleanup_pointer(a->cleanup_metadata); - - if (!cleanups || _upb_ArenaHas(a) < sizeof(cleanup_ent)) { - if (!upb_Arena_Allocblock(a, 128)) return false; /* Out of memory. */ - UPB_ASSERT(_upb_ArenaHas(a) >= sizeof(cleanup_ent)); - cleanups = upb_cleanup_pointer(a->cleanup_metadata); - } - - a->head.end -= sizeof(cleanup_ent); - ent = (cleanup_ent*)a->head.end; - (*cleanups)++; - UPB_UNPOISON_MEMORY_REGION(ent, sizeof(cleanup_ent)); - - ent->cleanup = func; - ent->ud = ud; - - return true; -} - -bool upb_Arena_Fuse(upb_Arena* a1, upb_Arena* a2) { - upb_Arena* r1 = arena_findroot(a1); - upb_Arena* r2 = arena_findroot(a2); - - if (r1 == r2) return true; /* Already fused. */ - - /* Do not fuse initial blocks since we cannot lifetime extend them. */ - if (upb_cleanup_has_initial_block(r1->cleanup_metadata)) return false; - if (upb_cleanup_has_initial_block(r2->cleanup_metadata)) return false; - - /* Only allow fuse with a common allocator */ - if (r1->block_alloc != r2->block_alloc) return false; - - /* We want to join the smaller tree to the larger tree. - * So swap first if they are backwards. */ - if (r1->refcount < r2->refcount) { - upb_Arena* tmp = r1; - r1 = r2; - r2 = tmp; - } - - /* r1 takes over r2's freelist and refcount. */ - r1->refcount += r2->refcount; - if (r2->freelist_tail) { - UPB_ASSERT(r2->freelist_tail->next == NULL); - r2->freelist_tail->next = r1->freelist; - r1->freelist = r2->freelist; - } - r2->parent = r1; - return true; -} - /* Miscellaneous utilities ****************************************************/ static void upb_FixLocale(char* p) { diff --git a/upb/upb.h b/upb/upb.h index d1ac913026..7398d09fff 100644 --- a/upb/upb.h +++ b/upb/upb.h @@ -39,34 +39,17 @@ #include #include +// TODO(b/232091617): Remove these and fix everything that breaks as a result. +#include "upb/arena.h" +#include "upb/status.h" + +// Must be last. #include "upb/port_def.inc" #ifdef __cplusplus extern "C" { #endif -/* upb_Status *****************************************************************/ - -#define _kUpb_Status_MaxMessage 127 - -typedef struct { - bool ok; - char msg[_kUpb_Status_MaxMessage]; /* Error message; NULL-terminated. */ -} upb_Status; - -const char* upb_Status_ErrorMessage(const upb_Status* status); -bool upb_Status_IsOk(const upb_Status* status); - -/* These are no-op if |status| is NULL. */ -void upb_Status_Clear(upb_Status* status); -void upb_Status_SetErrorMessage(upb_Status* status, const char* msg); -void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...) - UPB_PRINTF(2, 3); -void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt, - va_list args) UPB_PRINTF(2, 0); -void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt, - va_list args) UPB_PRINTF(2, 0); - /** upb_StringView ************************************************************/ typedef struct { @@ -96,183 +79,6 @@ UPB_INLINE bool upb_StringView_IsEqual(upb_StringView a, upb_StringView b) { #define UPB_STRINGVIEW_FORMAT "%.*s" #define UPB_STRINGVIEW_ARGS(view) (int)(view).size, (view).data -/** upb_alloc *****************************************************************/ - -/* A upb_alloc is a possibly-stateful allocator object. - * - * It could either be an arena allocator (which doesn't require individual - * free() calls) or a regular malloc() (which does). The client must therefore - * free memory unless it knows that the allocator is an arena allocator. */ - -struct upb_alloc; -typedef struct upb_alloc upb_alloc; - -/* A malloc()/free() function. - * If "size" is 0 then the function acts like free(), otherwise it acts like - * realloc(). Only "oldsize" bytes from a previous allocation are preserved. */ -typedef void* upb_alloc_func(upb_alloc* alloc, void* ptr, size_t oldsize, - size_t size); - -struct upb_alloc { - upb_alloc_func* func; -}; - -UPB_INLINE void* upb_malloc(upb_alloc* alloc, size_t size) { - UPB_ASSERT(alloc); - return alloc->func(alloc, NULL, 0, size); -} - -UPB_INLINE void* upb_realloc(upb_alloc* alloc, void* ptr, size_t oldsize, - size_t size) { - UPB_ASSERT(alloc); - return alloc->func(alloc, ptr, oldsize, size); -} - -UPB_INLINE void upb_free(upb_alloc* alloc, void* ptr) { - assert(alloc); - alloc->func(alloc, ptr, 0, 0); -} - -/* The global allocator used by upb. Uses the standard malloc()/free(). */ - -extern upb_alloc upb_alloc_global; - -/* Functions that hard-code the global malloc. - * - * We still get benefit because we can put custom logic into our global - * allocator, like injecting out-of-memory faults in debug/testing builds. */ - -UPB_INLINE void* upb_gmalloc(size_t size) { - return upb_malloc(&upb_alloc_global, size); -} - -UPB_INLINE void* upb_grealloc(void* ptr, size_t oldsize, size_t size) { - return upb_realloc(&upb_alloc_global, ptr, oldsize, size); -} - -UPB_INLINE void upb_gfree(void* ptr) { upb_free(&upb_alloc_global, ptr); } - -/* upb_Arena ******************************************************************/ - -/* upb_Arena is a specific allocator implementation that uses arena allocation. - * The user provides an allocator that will be used to allocate the underlying - * arena blocks. Arenas by nature do not require the individual allocations - * to be freed. However the Arena does allow users to register cleanup - * functions that will run when the arena is destroyed. - * - * A upb_Arena is *not* thread-safe. - * - * You could write a thread-safe arena allocator that satisfies the - * upb_alloc interface, but it would not be as efficient for the - * single-threaded case. */ - -typedef void upb_CleanupFunc(void* ud); - -struct upb_Arena; -typedef struct upb_Arena upb_Arena; - -typedef struct { - /* We implement the allocator interface. - * This must be the first member of upb_Arena! - * TODO(haberman): remove once handlers are gone. */ - upb_alloc alloc; - - char *ptr, *end; -} _upb_ArenaHead; - -/* Creates an arena from the given initial block (if any -- n may be 0). - * Additional blocks will be allocated from |alloc|. If |alloc| is NULL, this - * is a fixed-size arena and cannot grow. */ -upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc); -void upb_Arena_Free(upb_Arena* a); -bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func); -bool upb_Arena_Fuse(upb_Arena* a, upb_Arena* b); -void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size); - -UPB_INLINE upb_alloc* upb_Arena_Alloc(upb_Arena* a) { return (upb_alloc*)a; } - -UPB_INLINE size_t _upb_ArenaHas(upb_Arena* a) { - _upb_ArenaHead* h = (_upb_ArenaHead*)a; - return (size_t)(h->end - h->ptr); -} - -UPB_INLINE void* _upb_Arena_FastMalloc(upb_Arena* a, size_t size) { - _upb_ArenaHead* h = (_upb_ArenaHead*)a; - void* ret = h->ptr; - UPB_ASSERT(UPB_ALIGN_MALLOC((uintptr_t)ret) == (uintptr_t)ret); - UPB_ASSERT(UPB_ALIGN_MALLOC(size) == size); - UPB_UNPOISON_MEMORY_REGION(ret, size); - - h->ptr += size; - -#if UPB_ASAN - { - size_t guard_size = 32; - if (_upb_ArenaHas(a) >= guard_size) { - h->ptr += guard_size; - } else { - h->ptr = h->end; - } - } -#endif - - return ret; -} - -UPB_INLINE void* upb_Arena_Malloc(upb_Arena* a, size_t size) { - size = UPB_ALIGN_MALLOC(size); - - if (UPB_UNLIKELY(_upb_ArenaHas(a) < size)) { - return _upb_Arena_SlowMalloc(a, size); - } - - return _upb_Arena_FastMalloc(a, size); -} - -// Shrinks the last alloc from arena. -// REQUIRES: (ptr, oldsize) was the last malloc/realloc from this arena. -// We could also add a upb_Arena_TryShrinkLast() which is simply a no-op if -// this was not the last alloc. -UPB_INLINE void upb_Arena_ShrinkLast(upb_Arena* a, void* ptr, size_t oldsize, - size_t size) { - _upb_ArenaHead* h = (_upb_ArenaHead*)a; - oldsize = UPB_ALIGN_MALLOC(oldsize); - size = UPB_ALIGN_MALLOC(size); - UPB_ASSERT((char*)ptr + oldsize == h->ptr); // Must be the last alloc. - UPB_ASSERT(size <= oldsize); - h->ptr = (char*)ptr + size; -} - -UPB_INLINE void* upb_Arena_Realloc(upb_Arena* a, void* ptr, size_t oldsize, - size_t size) { - _upb_ArenaHead* h = (_upb_ArenaHead*)a; - oldsize = UPB_ALIGN_MALLOC(oldsize); - size = UPB_ALIGN_MALLOC(size); - bool is_most_recent_alloc = (uintptr_t)ptr + oldsize == (uintptr_t)h->ptr; - - if (is_most_recent_alloc) { - ptrdiff_t diff = size - oldsize; - if ((ptrdiff_t)_upb_ArenaHas(a) >= diff) { - h->ptr += diff; - return ptr; - } - } else if (size <= oldsize) { - return ptr; - } - - void* ret = upb_Arena_Malloc(a, size); - - if (ret && oldsize > 0) { - memcpy(ret, ptr, UPB_MIN(oldsize, size)); - } - - return ret; -} - -UPB_INLINE upb_Arena* upb_Arena_New(void) { - return upb_Arena_Init(NULL, 0, &upb_alloc_global); -} - /* Constants ******************************************************************/ /* A list of types as they are encoded on-the-wire. */ diff --git a/upb/upb_internal.h b/upb/upb_internal.h index 1eb166f7d3..6c1fec3398 100644 --- a/upb/upb_internal.h +++ b/upb/upb_internal.h @@ -28,41 +28,9 @@ #ifndef UPB_INT_H_ #define UPB_INT_H_ -#include "upb/upb.h" +// TODO(b/232091617): Delete this entire header which currently exists only for +// temporary backwards compatibility. -struct mem_block; -typedef struct mem_block mem_block; - -struct upb_Arena { - _upb_ArenaHead head; - /* Stores cleanup metadata for this arena. - * - a pointer to the current cleanup counter. - * - a boolean indicating if there is an unowned initial block. */ - uintptr_t cleanup_metadata; - - /* Allocator to allocate arena blocks. We are responsible for freeing these - * when we are destroyed. */ - upb_alloc* block_alloc; - uint32_t last_size; - - /* When multiple arenas are fused together, each arena points to a parent - * arena (root points to itself). The root tracks how many live arenas - * reference it. */ - uint32_t refcount; /* Only used when a->parent == a */ - struct upb_Arena* parent; - - /* Linked list of blocks to free/cleanup. */ - mem_block *freelist, *freelist_tail; -}; - -// Encodes a float or double that is round-trippable, but as short as possible. -// These routines are not fully optimal (not guaranteed to be shortest), but are -// short-ish and match the implementation that has been used in protobuf since -// the beginning. -// -// The given buffer size must be at least kUpb_RoundTripBufferSize. -enum { kUpb_RoundTripBufferSize = 32 }; -void _upb_EncodeRoundTripDouble(double val, char* buf, size_t size); -void _upb_EncodeRoundTripFloat(float val, char* buf, size_t size); +#include "upb/internal/upb.h" #endif /* UPB_INT_H_ */