diff --git a/fontique/src/collection/mod.rs b/fontique/src/collection/mod.rs index f873a511..efd4c114 100644 --- a/fontique/src/collection/mod.rs +++ b/fontique/src/collection/mod.rs @@ -85,6 +85,12 @@ impl Collection { } } + /// Converts an unshared collection into a shared collection + #[cfg(feature = "std")] + pub fn make_shared(&mut self) { + self.inner.make_shared(); + } + /// Load system fonts. If system fonts are already loaded then this does nothing. pub fn load_system_fonts(&mut self) { if self.inner.system.is_none() { @@ -254,6 +260,16 @@ impl Inner { } } + #[cfg(feature = "std")] + pub fn make_shared(&mut self) { + if self.shared.is_none() { + self.shared = Some(Arc::new(Shared { + data: Mutex::new(core::mem::take(&mut self.data)), + version: AtomicCounter::new(self.shared_version), + })); + } + } + /// Load system fonts. If system fonts are already loaded then they will be reloaded. pub fn load_system_fonts(&mut self) { self.system = Some(System::new()); diff --git a/fontique/src/source_cache.rs b/fontique/src/source_cache.rs index 734c9372..db8e5b72 100644 --- a/fontique/src/source_cache.rs +++ b/fontique/src/source_cache.rs @@ -77,6 +77,15 @@ impl SourceCache { } } + /// Turns an unshared cache into a shared cache that can used to ensure that fonts only get loaded once + /// even when they are loaded across multiple threads. + #[cfg(feature = "std")] + pub fn make_shared(&mut self) { + if self.shared.is_none() { + self.shared = Some(Arc::new(Mutex::new(Shared::from_local(&self.cache)))); + } + } + /// Returns the [blob] for the given font data, attempting to load /// it from the file system if not already present. /// @@ -159,6 +168,17 @@ struct Shared { #[cfg(feature = "std")] impl Shared { + /// Bootstrap a shared cache from a local one + fn from_local(unshared: &HashMap>>) -> Self { + let shared_cache: HashMap>> = unshared + .iter() + .map(|(key, value)| (*key, value.into())) + .collect(); + Self { + cache: shared_cache, + } + } + pub fn get(&mut self, id: SourceId, path: &Path) -> Option> { use hashbrown::hash_map::Entry as HashEntry; match self.cache.entry(id) { @@ -213,6 +233,19 @@ struct EntryData { serial: u64, } +#[cfg(feature = "std")] +impl From<&Entry>> for Entry> { + fn from(value: &Entry>) -> Self { + match value { + Entry::Loaded(entry_data) => Self::Loaded(EntryData { + font_data: entry_data.font_data.downgrade(), + serial: entry_data.serial, + }), + Entry::Failed => Self::Failed, + } + } +} + #[cfg(feature = "std")] pub(crate) fn load_blob(path: &Path) -> Option> { let file = std::fs::File::open(path).ok()?;