|  | // Protocol Buffers - Google's data interchange format | 
|  | // Copyright 2023 Google LLC.  All rights reserved. | 
|  | // | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file or at | 
|  | // https://developers.google.com/open-source/licenses/bsd | 
|  |  | 
|  | #include "upb/mini_table/extension_registry.h" | 
|  |  | 
|  | #include "upb/hash/str_table.h" | 
|  | #include "upb/mini_table/extension.h" | 
|  |  | 
|  | // Must be last. | 
|  | #include "upb/port/def.inc" | 
|  |  | 
|  | #define EXTREG_KEY_SIZE (sizeof(upb_MiniTable*) + sizeof(uint32_t)) | 
|  |  | 
|  | struct upb_ExtensionRegistry { | 
|  | upb_Arena* arena; | 
|  | upb_strtable exts;  // Key is upb_MiniTable* concatenated with fieldnum. | 
|  | }; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | UPB_API bool upb_ExtensionRegistry_Add(upb_ExtensionRegistry* r, | 
|  | const upb_MiniTableExtension* e) { | 
|  | char buf[EXTREG_KEY_SIZE]; | 
|  | extreg_key(buf, e->extendee, e->field.number); | 
|  | if (upb_strtable_lookup2(&r->exts, buf, EXTREG_KEY_SIZE, NULL)) return false; | 
|  | return upb_strtable_insert(&r->exts, buf, EXTREG_KEY_SIZE, | 
|  | upb_value_constptr(e), r->arena); | 
|  | } | 
|  |  | 
|  | bool upb_ExtensionRegistry_AddArray(upb_ExtensionRegistry* r, | 
|  | const upb_MiniTableExtension** e, | 
|  | size_t count) { | 
|  | const upb_MiniTableExtension** start = e; | 
|  | const upb_MiniTableExtension** end = UPB_PTRADD(e, count); | 
|  | for (; e < end; e++) { | 
|  | if (!upb_ExtensionRegistry_Add(r, *e)) goto failure; | 
|  | } | 
|  | return true; | 
|  |  | 
|  | failure: | 
|  | // Back out the entries previously added. | 
|  | for (end = e, e = start; e < end; e++) { | 
|  | const upb_MiniTableExtension* ext = *e; | 
|  | char buf[EXTREG_KEY_SIZE]; | 
|  | extreg_key(buf, ext->extendee, ext->field.number); | 
|  | upb_strtable_remove2(&r->exts, buf, EXTREG_KEY_SIZE, NULL); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const upb_MiniTableExtension* upb_ExtensionRegistry_Lookup( | 
|  | const upb_ExtensionRegistry* r, const upb_MiniTable* t, uint32_t num) { | 
|  | char buf[EXTREG_KEY_SIZE]; | 
|  | upb_value v; | 
|  | extreg_key(buf, t, num); | 
|  | if (upb_strtable_lookup2(&r->exts, buf, EXTREG_KEY_SIZE, &v)) { | 
|  | return upb_value_getconstptr(v); | 
|  | } else { | 
|  | return NULL; | 
|  | } | 
|  | } |