Introduced a generated extension registry using linker arrays.
PiperOrigin-RevId: 640196225
diff --git a/upb/mini_table/extension_registry.c b/upb/mini_table/extension_registry.c
index f5b118f..6916f7e 100644
--- a/upb/mini_table/extension_registry.c
+++ b/upb/mini_table/extension_registry.c
@@ -70,6 +70,24 @@
return false;
}
+#ifdef UPB_LINKARR_DECLARE
+
+UPB_LINKARR_DECLARE(upb_AllExts, const upb_MiniTableExtension*);
+
+bool upb_ExtensionRegistry_AddAllLinkedExtensions(upb_ExtensionRegistry* r) {
+ const upb_MiniTableExtension* const* start = UPB_LINKARR_START(upb_AllExts);
+ const upb_MiniTableExtension* const* stop = UPB_LINKARR_STOP(upb_AllExts);
+ for (const upb_MiniTableExtension* const* p = start; p < stop; p++) {
+ // Windows can introduce zero padding, so we have to skip zeroes.
+ if (*p != 0) {
+ if (!upb_ExtensionRegistry_Add(r, *p)) return false;
+ }
+ }
+ return true;
+}
+
+#endif // UPB_LINKARR_DECLARE
+
const upb_MiniTableExtension* upb_ExtensionRegistry_Lookup(
const upb_ExtensionRegistry* r, const upb_MiniTable* t, uint32_t num) {
char buf[EXTREG_KEY_SIZE];
diff --git a/upb/mini_table/extension_registry.h b/upb/mini_table/extension_registry.h
index 0465140..9f5f81a 100644
--- a/upb/mini_table/extension_registry.h
+++ b/upb/mini_table/extension_registry.h
@@ -71,6 +71,23 @@
const upb_MiniTableExtension** e,
size_t count);
+#ifdef UPB_LINKARR_DECLARE
+
+// Adds all extensions linked into the binary into the registry. The set of
+// linked extensions is assembled by the linker using linker arrays. This
+// will likely not work properly if the extensions are split across multiple
+// shared libraries.
+//
+// Returns true if all extensions were added successfully, false on out of
+// memory or if any extensions were already present.
+//
+// This API is currently not available on MSVC (though it *is* available on
+// Windows using clang-cl).
+UPB_API bool upb_ExtensionRegistry_AddAllLinkedExtensions(
+ upb_ExtensionRegistry* r);
+
+#endif // UPB_LINKARR_DECLARE
+
// Looks up the extension (if any) defined for message type |t| and field
// number |num|. Returns the extension if found, otherwise NULL.
UPB_API const upb_MiniTableExtension* upb_ExtensionRegistry_Lookup(
diff --git a/upb/port/def.inc b/upb/port/def.inc
index 7d23d5c..0480da7 100644
--- a/upb/port/def.inc
+++ b/upb/port/def.inc
@@ -350,3 +350,79 @@
error UPB_TRACING_ENABLED Tracing should be disabled in production builds
#endif
#endif
+
+// Linker arrays combine elements from multiple translation units into a single
+// array that can be iterated over at runtime.
+//
+// It is an alternative to pre-main "registration" functions.
+//
+// Usage:
+//
+// // In N translation units.
+// UPB_LINKARR_APPEND(foo_array) static int elems[3] = {1, 2, 3};
+//
+// // At runtime:
+// UPB_LINKARR_DECLARE(foo_array, int);
+//
+// void f() {
+// const int* start = UPB_LINKARR_START(foo_array);
+// const int* stop = UPB_LINKARR_STOP(foo_array);
+// for (const int* p = start; p < stop; p++) {
+// // Windows can introduce zero padding, so we have to skip zeroes.
+// if (*p != 0) {
+// vec.push_back(*p);
+// }
+// }
+// }
+
+#if defined(__ELF__) || defined(__wasm__)
+
+#define UPB_LINKARR_APPEND(name) \
+ __attribute__((retain, used, section("linkarr_" #name)))
+#define UPB_LINKARR_DECLARE(name, type) \
+ extern type const __start_linkarr_##name; \
+ extern type const __stop_linkarr_##name; \
+ UPB_LINKARR_APPEND(name) type UPB_linkarr_internal_empty_##name[1] = {0}
+#define UPB_LINKARR_START(name) (&__start_linkarr_##name)
+#define UPB_LINKARR_STOP(name) (&__stop_linkarr_##name)
+
+#elif defined(__MACH__)
+
+/* As described in: https://stackoverflow.com/a/22366882 */
+#define UPB_LINKARR_APPEND(name) \
+ __attribute__((retain, used, section("__DATA,la_" #name)))
+#define UPB_LINKARR_DECLARE(name, type) \
+ extern type const __start_linkarr_##name __asm( \
+ "section$start$__DATA$la_" #name); \
+ extern type const __stop_linkarr_##name __asm( \
+ "section$end$__DATA$" \
+ "la_" #name); \
+ UPB_LINKARR_APPEND(name) type UPB_linkarr_internal_empty_##name[1] = {0}
+#define UPB_LINKARR_START(name) (&__start_linkarr_##name)
+#define UPB_LINKARR_STOP(name) (&__stop_linkarr_##name)
+
+#elif defined(_MSC_VER) && defined(__clang__)
+
+/* See:
+ * https://devblogs.microsoft.com/oldnewthing/20181107-00/?p=100155
+ * https://devblogs.microsoft.com/oldnewthing/20181108-00/?p=100165
+ * https://devblogs.microsoft.com/oldnewthing/20181109-00/?p=100175 */
+
+// Usage of __attribute__ here probably means this is Clang-specific, and would
+// not work on MSVC.
+#define UPB_LINKARR_APPEND(name) \
+ __declspec(allocate("la_" #name "$j")) __attribute__((retain, used))
+#define UPB_LINKARR_DECLARE(name, type) \
+ __declspec(allocate("la_" #name "$a")) type __start_linkarr_##name; \
+ __declspec(allocate("la_" #name "$z")) type __stop_linkarr_##name; \
+ UPB_LINKARR_APPEND(name) type UPB_linkarr_internal_empty_##name[1] = {0}
+#define UPB_LINKARR_START(name) (&__start_linkarr_##name)
+#define UPB_LINKARR_STOP(name) (&__stop_linkarr_##name)
+
+#else
+
+// Linker arrays are not supported on this platform. Make appends a no-op but
+// don't define the other macros.
+#define UPB_LINKARR_APPEND(name)
+
+#endif
diff --git a/upb/port/undef.inc b/upb/port/undef.inc
index c715441..d8453ff 100644
--- a/upb/port/undef.inc
+++ b/upb/port/undef.inc
@@ -56,3 +56,7 @@
#undef UPB_USE_C11_ATOMICS
#undef UPB_PRIVATE
#undef UPB_ONLYBITS
+#undef UPB_LINKARR_DECLARE
+#undef UPB_LINKARR_APPEND
+#undef UPB_LINKARR_START
+#undef UPB_LINKARR_STOP
diff --git a/upb_generator/protoc-gen-upb_minitable.cc b/upb_generator/protoc-gen-upb_minitable.cc
index 86f5b69..7c15308 100644
--- a/upb_generator/protoc-gen-upb_minitable.cc
+++ b/upb_generator/protoc-gen-upb_minitable.cc
@@ -519,6 +519,7 @@
output(
"\n"
+ "UPB_LINKARR_APPEND(upb_AllExts)\n"
"static const upb_MiniTableExtension *$0[$1] = {\n",
kExtensionsInit, exts.size());