diff --git a/src/archive/interner.rs b/src/archive/interner.rs new file mode 100644 index 0000000..84ca455 --- /dev/null +++ b/src/archive/interner.rs @@ -0,0 +1,35 @@ +//! interner module. + +use std::collections::HashSet; +use std::sync::RwLock; + +pub struct Interner(RwLock>); + +impl Interner +where + T: Eq + std::hash::Hash, +{ + pub fn new() -> Self { + Self(RwLock::new(HashSet::new())) + } + + fn get(&self, value: &T) -> Option<&'static T> { + let store = self.0.read().unwrap(); + + store.get(value).map(|value| *value) + } + + fn set(&self, value: T) -> &'static T { + let boxed: Box = Box::new(value); + let leaked: &'static T = Box::leak(boxed); + let mut store = self.0.write().unwrap(); + + store.insert(leaked); + + leaked + } + + pub fn leak(&self, value: T) -> &'static T { + self.get(&value).unwrap_or_else(|| self.set(value)) + } +} diff --git a/src/archive/mod.rs b/src/archive/mod.rs index 1216681..0882039 100644 --- a/src/archive/mod.rs +++ b/src/archive/mod.rs @@ -1,6 +1,7 @@ //! Archive module. mod fetcher; +mod interner; mod parser; mod story; diff --git a/src/archive/story.rs b/src/archive/story.rs index 092a738..50ac36e 100644 --- a/src/archive/story.rs +++ b/src/archive/story.rs @@ -1,18 +1,15 @@ //! Story meta. -use std::collections::HashSet; -use std::sync::Mutex; - use chrono::prelude::*; - +use lazy_static::lazy_static; use serde::de::Error; use serde::{Deserialize, Deserializer}; use serde_json::Value; -use lazy_static::lazy_static; +use super::interner::Interner; lazy_static! { - static ref TAGS: Mutex> = Mutex::new(HashSet::new()); + static ref TAGS: Interner = Interner::new(); } #[derive(Clone, Debug, Deserialize)] @@ -43,7 +40,7 @@ pub struct Story { pub short_description: String, pub status: Status, pub submitted: bool, - #[serde(deserialize_with = "interned_tag")] + #[serde(deserialize_with = "tags_as_static")] pub tags: Vec<&'static Tag>, #[serde(deserialize_with = "null_to_text")] pub title: String, @@ -200,25 +197,13 @@ where } } -fn interned_tag<'de, D>(d: D) -> Result, D::Error> +fn tags_as_static<'de, D>(d: D) -> Result, D::Error> where D: Deserializer<'de>, { - let tags = Vec::::deserialize(d)?; - let mut store = TAGS.lock().unwrap(); + let tags: Vec = Vec::deserialize(d)?; - Ok(tags - .into_iter() - .map(|tag| match store.get(&tag) { - Some(tag) => tag, - None => { - let boxed: Box = Box::new(tag); - let leaked: &'static Tag = Box::leak(boxed); - store.insert(leaked); - leaked - } - }) - .collect()) + Ok(tags.into_iter().map(|tag| TAGS.leak(tag)).collect()) } impl<'de> Deserialize<'de> for Color {