diff --git a/Cargo.toml b/Cargo.toml index d45df2cc..8e6655a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ quick-xml = "0.22.0" rayon = { version = "1.3.0", optional = true } kurbo = { version = "0.8.1", optional = true } thiserror = "1.0" +crossbeam-channel = "0.5" [dependencies.druid] #git = "https://github.com/xi-editor/druid.git" diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 1fd25c97..3b46e690 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -6,6 +6,7 @@ mod serialize; #[cfg(test)] mod tests; +use std::io::Read; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -64,18 +65,20 @@ impl Glyph { /// between instances. pub fn load(path: impl AsRef) -> Result { let path = path.as_ref(); - let names = NameList::default(); - Glyph::load_with_names(path, &names) - } - - /// Attempt to load the glyph at `path`, reusing names from the `NameList`. - /// - /// This uses string interning to reuse allocations when a glyph name - /// occurs multiple times (such as in components or in different layers). - pub fn load_with_names(path: &Path, names: &NameList) -> Result { - std::fs::read(path) - .map_err(GlifLoadError::Io) - .and_then(|data| parse::GlifParser::from_xml(&data, Some(names))) + Glyph::load_impl(path, None, |_| ()) + } + + pub(crate) fn load_impl( + path: &Path, + names: Option<&NameList>, + drop_file_handle_fn: impl FnOnce(std::fs::File), + ) -> Result { + let mut file = std::fs::File::open(path).map_err(GlifLoadError::Io)?; + let mut buf = String::new(); + file.read_to_string(&mut buf).map_err(GlifLoadError::Io)?; + let result = parse::GlifParser::from_xml(buf.as_bytes(), names); + drop_file_handle_fn(file); + result } #[doc(hidden)] diff --git a/src/glyph/parse.rs b/src/glyph/parse.rs index e4b8eb98..f1e28efe 100644 --- a/src/glyph/parse.rs +++ b/src/glyph/parse.rs @@ -53,7 +53,7 @@ impl<'names> GlifParser<'names> { let tag_name = reader.decode(start.name())?; match tag_name.borrow() { "outline" => self.parse_outline(reader, buf)?, - "lib" => self.parse_lib(reader, raw_xml, buf)?, // do this at some point? + "lib" => self.parse_lib(reader, raw_xml, buf)?, "note" => self.parse_note(reader, buf)?, _other => return Err(ErrorKind::UnexpectedTag.into()), } diff --git a/src/layer.rs b/src/layer.rs index 21f18332..0b144094 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -1,8 +1,8 @@ -use std::borrow::Borrow; use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; +use std::{borrow::Borrow, fs::File}; #[cfg(feature = "rayon")] use rayon::prelude::*; @@ -49,20 +49,36 @@ impl LayerSet { vec![(Arc::from(DEFAULT_LAYER_NAME), PathBuf::from(DEFAULT_GLYPHS_DIRNAME))] }; + let (tx, rx) = crossbeam_channel::unbounded(); + let _ = std::thread::spawn(move || { + loop { + match rx.recv() { + Err(_) => return, + Ok(handle) => { + //eprintln!("droping {:?}", &handle); + std::mem::drop(handle); + } + } + } + }); + let mut layers: Vec<_> = to_load .into_iter() .map(|(name, path)| { let layer_path = base_dir.join(&path); - Layer::load_impl(&layer_path, name, glyph_names) + Layer::load_impl(&layer_path, name, glyph_names, Some(&tx)) }) .collect::>()?; + // this closes the rx side, finishing the thread + std::mem::drop(tx); // move the default layer to the front let default_idx = layers .iter() .position(|l| l.path.to_str() == Some(DEFAULT_GLYPHS_DIRNAME)) .ok_or(Error::MissingDefaultLayer)?; layers.rotate_left(default_idx); + //handle.join().unwrap(); Ok(LayerSet { layers }) } @@ -228,7 +244,7 @@ impl Layer { pub fn load(path: impl AsRef, name: LayerName) -> Result { let path = path.as_ref(); let names = NameList::default(); - Layer::load_impl(path, name, &names) + Layer::load_impl(path, name, &names, None) } /// The actual loading logic. @@ -239,6 +255,7 @@ impl Layer { path: &Path, name: LayerName, names: &NameList, + drop_thread: Option<&crossbeam_channel::Sender>, ) -> Result { let contents_path = path.join(CONTENTS_FILE); if !contents_path.exists() { @@ -259,12 +276,17 @@ impl Layer { let name = names.get(name); let glyph_path = path.join(glyph_path); - Glyph::load_with_names(&glyph_path, names) - .map(|mut glyph| { - glyph.name = name.clone(); - (name, Arc::new(glyph)) - }) - .map_err(|source| Error::GlifLoad { path: glyph_path, source }) + Glyph::load_impl(&glyph_path, Some(names), |file| { + if let Some(drop_chan) = drop_thread.as_ref() { + //println!("sending {:?}", &file); + drop_chan.send(file).unwrap(); + } + }) + .map(|mut glyph| { + glyph.name = name.clone(); + (name, Arc::new(glyph)) + }) + .map_err(|source| Error::GlifLoad { path: glyph_path, source }) }) .collect::>()?;