From 8000783f95ee007d9dda6f1dcc1c42c8e607b122 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 20 Jul 2021 18:35:05 +0200 Subject: FileId instead of Path + FileHash --- src/loading/fs.rs | 79 ++++++++++++++++++++++-------------------------------- src/loading/mod.rs | 41 +++++++++++----------------- 2 files changed, 48 insertions(+), 72 deletions(-) (limited to 'src/loading') diff --git a/src/loading/fs.rs b/src/loading/fs.rs index 7fa1c120..ea33016c 100644 --- a/src/loading/fs.rs +++ b/src/loading/fs.rs @@ -1,39 +1,43 @@ -use std::collections::{hash_map::Entry, HashMap}; -use std::fs::File; +use std::collections::HashMap; +use std::fs::{self, File}; use std::io; use std::path::{Path, PathBuf}; -use std::rc::Rc; use memmap2::Mmap; use same_file::Handle; -use serde::{Deserialize, Serialize}; use ttf_parser::{name_id, Face}; use walkdir::WalkDir; -use super::{Buffer, FileHash, Loader}; +use super::{FileId, Loader}; use crate::font::{FaceInfo, FontStretch, FontStyle, FontVariant, FontWeight}; +use crate::util::PathExt; /// Loads fonts and images from the local file system. /// /// _This is only available when the `fs` feature is enabled._ -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Default, Debug, Clone)] pub struct FsLoader { faces: Vec, - files: Vec, - #[serde(skip)] - cache: FileCache, + paths: HashMap, } -/// Maps from resolved file hashes to loaded file buffers. -type FileCache = HashMap; - impl FsLoader { /// Create a new loader without any fonts. pub fn new() -> Self { - Self { - faces: vec![], - files: vec![], - cache: HashMap::new(), + Self { faces: vec![], paths: HashMap::new() } + } + + /// Resolve a file id for a path. + pub fn resolve_path(&mut self, path: &Path) -> io::Result { + let file = File::open(path)?; + let meta = file.metadata()?; + if meta.is_file() { + let handle = Handle::from_file(file)?; + let id = FileId(fxhash::hash64(&handle)); + self.paths.insert(id, path.normalize()); + Ok(id) + } else { + Err(io::Error::new(io::ErrorKind::Other, "not a file")) } } @@ -149,17 +153,11 @@ impl FsLoader { stretch: FontStretch::from_number(face.width().to_number()), }; - // Merge with an existing entry for the same family name. - self.faces.push(FaceInfo { family, variant, index }); - self.files.push(path.to_owned()); + let file = self.resolve_path(path)?; + self.faces.push(FaceInfo { file, index, family, variant }); Ok(()) } - - /// Paths to font files, parallel to [`faces()`](Self::faces). - pub fn files(&self) -> &[PathBuf] { - &self.files - } } impl Loader for FsLoader { @@ -167,30 +165,14 @@ impl Loader for FsLoader { &self.faces } - fn load_face(&mut self, idx: usize) -> Option { - self.load_file(&self.files[idx].clone()) + fn resolve_from(&mut self, base: FileId, path: &Path) -> Option { + let dir = self.paths[&base].parent()?; + let full = dir.join(path); + self.resolve_path(&full).ok() } - fn load_file(&mut self, path: &Path) -> Option { - let hash = self.resolve(path)?; - Some(Rc::clone(match self.cache.entry(hash) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { - let buffer = std::fs::read(path).ok()?; - entry.insert(Rc::new(buffer)) - } - })) - } - - fn resolve(&self, path: &Path) -> Option { - let file = File::open(path).ok()?; - let meta = file.metadata().ok()?; - if meta.is_file() { - let handle = Handle::from_file(file).ok()?; - Some(FileHash::from_raw(fxhash::hash64(&handle))) - } else { - None - } + fn load_file(&mut self, id: FileId) -> Option> { + fs::read(&self.paths[&id]).ok() } } @@ -203,7 +185,10 @@ mod tests { let mut loader = FsLoader::new(); loader.search_path("fonts"); - assert_eq!(loader.files, &[ + let mut paths: Vec<_> = loader.paths.values().collect(); + paths.sort(); + + assert_eq!(paths, [ Path::new("fonts/EBGaramond-Bold.ttf"), Path::new("fonts/EBGaramond-BoldItalic.ttf"), Path::new("fonts/EBGaramond-Italic.ttf"), diff --git a/src/loading/mod.rs b/src/loading/mod.rs index 0e171796..c2f7ca39 100644 --- a/src/loading/mod.rs +++ b/src/loading/mod.rs @@ -7,44 +7,39 @@ mod fs; pub use fs::*; use std::path::Path; -use std::rc::Rc; -use crate::font::FaceInfo; +use serde::{Deserialize, Serialize}; -/// A shared byte buffer. -pub type Buffer = Rc>; +use crate::font::FaceInfo; /// Loads resources from a local or remote source. pub trait Loader { /// Descriptions of all font faces this loader serves. fn faces(&self) -> &[FaceInfo]; - /// Load the font face with the given index in [`faces()`](Self::faces). - fn load_face(&mut self, idx: usize) -> Option; - - /// Load a file from a path. - fn load_file(&mut self, path: &Path) -> Option; - - /// Resolve a hash for the file the path points to. + /// Resolve a `path` relative to a `base` file. /// - /// This should return the same hash for all paths pointing to the same file + /// This should return the same id for all paths pointing to the same file /// and `None` if the file does not exist. - fn resolve(&self, path: &Path) -> Option; + fn resolve_from(&mut self, base: FileId, path: &Path) -> Option; + + /// Load a file by id. + fn load_file(&mut self, id: FileId) -> Option>; } -/// A file hash that can be [resolved](Loader::resolve) from a path. +/// A file id that can be [resolved](Loader::resolve_from) from a path. /// /// Should be the same for all paths pointing to the same file. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct FileHash(u64); +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct FileId(u64); -impl FileHash { - /// Create an file hash from a raw hash value. +impl FileId { + /// Create a file id from a raw value. pub fn from_raw(v: u64) -> Self { Self(v) } - /// Convert into the raw underlying hash value. + /// Convert into the raw underlying value. pub fn into_raw(self) -> u64 { self.0 } @@ -58,15 +53,11 @@ impl Loader for BlankLoader { &[] } - fn load_face(&mut self, _: usize) -> Option { - None - } - - fn load_file(&mut self, _: &Path) -> Option { + fn resolve_from(&mut self, _: FileId, _: &Path) -> Option { None } - fn resolve(&self, _: &Path) -> Option { + fn load_file(&mut self, _: FileId) -> Option> { None } } -- cgit v1.2.3