| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2026 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 | |
| use super::upb_reflection::{self, DefPool, DefPoolInitPtr, MessageDef}; | |
| use super::{MiniTableEnumPtr, MiniTableExtensionPtr, MiniTablePtr, THREAD_LOCAL_ARENA}; | |
| use std::ffi::CStr; | |
| use std::sync::{Mutex, OnceLock}; | |
| #[derive(Clone, Copy)] | |
| pub struct DefPoolInit(DefPoolInitPtr); | |
| unsafe impl Send for DefPoolInit {} | |
| unsafe impl Sync for DefPoolInit {} | |
| /// Holds the MessageDef of one message once `message_def` has looked it up. | |
| #[derive(Default)] | |
| pub struct MessageDefCached(OnceLock<MessageDef<'static>>); | |
| unsafe impl Sync for MessageDefCached {} | |
| impl MessageDefCached { | |
| pub const fn new() -> Self { | |
| Self(OnceLock::new()) | |
| } | |
| } | |
| struct GlobalDefPool(DefPool); | |
| unsafe impl Send for GlobalDefPool {} | |
| // There is only one global, singleton DefPool, protected by a Mutex. | |
| static POOL: OnceLock<Mutex<GlobalDefPool>> = OnceLock::new(); | |
| /// A message of which a MessageDef and DefPoolInit can be looked up. This trait must be | |
| /// implemented in Rust gencode for every message that needs reflection support. | |
| /// | |
| /// # Safety | |
| /// - `FULL_NAME` must be the message's fully-qualified name. | |
| /// - `def_init()` must return the init of the .proto file that declares the message. | |
| /// - `message_def_cached()` must return a static MessageDef. | |
| pub unsafe trait UpbWithReflection { | |
| const FULL_NAME: &'static str; | |
| fn def_init() -> DefPoolInit; | |
| // For each message type, there is only one static, cached pointer to its MessageDef. | |
| fn message_def_cached() -> &'static MessageDefCached; | |
| } | |
| /// Builds a upb_DefPool_Init | |
| /// | |
| /// # Safety | |
| /// - `filename` and `descriptor` must be the file's name and its serialized `FileDescriptorProto`. | |
| /// - `deps` must be the inits of the files it imports. | |
| /// - `msgs`, `enums` and `exts` must be the file's own MiniTables, in the order upb's generator | |
| /// lists them in a `upb_MiniTableFile` (see `SortedMessages`, `SortedEnums` and | |
| /// `SortedExtensions` in upb_generator/file_layout.h). | |
| pub unsafe fn build_def_init( | |
| filename: &'static CStr, | |
| descriptor: &'static [u8], | |
| deps: &[DefPoolInit], | |
| msgs: &[MiniTablePtr], | |
| enums: &[MiniTableEnumPtr], | |
| exts: &[MiniTableExtensionPtr], | |
| ) -> DefPoolInit { | |
| let mut raw_deps: Vec<DefPoolInitPtr> = Vec::with_capacity(deps.len()); | |
| for dep in deps { | |
| raw_deps.push(dep.0); | |
| } | |
| THREAD_LOCAL_ARENA.with(|arena| unsafe { | |
| let layout = upb_reflection::build_mini_table_file(arena, msgs, enums, exts); | |
| DefPoolInit(upb_reflection::build_def_pool_init( | |
| arena, filename, descriptor, &raw_deps, layout, | |
| )) | |
| }) | |
| } | |
| /// Returns the MessageDef of T. | |
| pub fn message_def<T: UpbWithReflection>() -> MessageDef<'static> { | |
| *(T::message_def_cached().0.get_or_init(|| { | |
| // Eventually calls `build_def_init` emitted for `T`. | |
| let init = T::def_init(); | |
| let mut pool = POOL | |
| .get_or_init(|| Mutex::new(GlobalDefPool(DefPool::new()))) | |
| .lock() | |
| .expect("global DefPool mutex should not be poisoned"); | |
| // Load the descriptor into the global DefPool. If it was already loaded, this would be a | |
| // no-op. | |
| // | |
| // SAFETY: `init` was only built for the file that declares `T`. | |
| let loaded = unsafe { pool.0.load_def_init(init.0.as_ptr()) }; | |
| assert!(loaded, "failed to load a generated descriptor into the global DefPool"); | |
| let def = pool | |
| .0 | |
| .find_message_by_name(T::FULL_NAME) | |
| .expect("a generated message is missing from its own file's descriptor"); | |
| // SAFETY: `def` was allocated by the global pool, which is never freed. | |
| unsafe { MessageDef::from_raw(def.raw()) } | |
| })) | |
| } |