unobtanium 3.0.0

Opinioated Web search engine library with crawler and viewer companion.
Documentation

use std::collections::hash_map::HashMap;
use std::hash::Hash;

use uuid::Uuid;

use crate::database::id::*;
use crate::url::UrlWithoutFragment;


pub type UrlCache = IdCache<UrlWithoutFragment, UrlId>;
pub type EntityGenerationUuidCache = IdCache<Uuid, EntityGenerationId>;

/// A general purpose cache for mapping database internal indentifiers to
/// external ones and vice versa.
///
/// Stores up to `size` entries and purges the least recent half of the cache if
/// the cache would have to store more entries.
pub struct IdCache<
	T: Eq + Hash + Clone,
	I: NumericDatabseId + Eq + Hash + Clone + Copy
> {
	data_to_id: HashMap<T, I>,
	id_to_data: HashMap<I, T>,
	queue: [Option<I>; 2048],
	offset: usize
}

impl<T, I> IdCache<T, I>
where
	T: Eq + Hash + Clone,
	I: NumericDatabseId + Eq + Hash + Clone + Copy
{
	
	pub fn new() -> Self {
		Self {
			data_to_id: HashMap::with_capacity(2048),
			id_to_data: HashMap::with_capacity(2048),
			offset: 0,
			queue: [None; 2048],
		}
	}

	#[inline(always)]
	pub fn bump_offset(&mut self) {
		self.offset = (self.offset + 1) & (2048-1);
	}

	pub fn push(&mut self, id: I, data: &T) {
		//println!("[data_cache] push");
		if self.id_to_data.insert(id, data.clone()).is_none() {
			self.data_to_id.insert(data.clone(), id);
			//println!("[data_cache] push_hit");
			self.queue[self.offset] = Some(id);
			self.bump_offset();
		} else {
			return;
		}
		if self.queue[self.offset].is_none() { return; }
		let mut c: usize = 0;
		while c < 1024 {
			if let Some(id) = self.queue[self.offset] {
				self.remove_by_id(id);
				self.queue[self.offset] = None;
				self.bump_offset();
				c += 1;
			} else {
				break; // should never happen, but …
			}
		}
	}

	fn remove_by_id(&mut self, id: I) {
		//println!("[data_cache] remove");
		if let Some(data) = self.id_to_data.get(&id) {
			self.data_to_id.remove(data);
			self.id_to_data.remove(&id);
		}
	}

	pub fn get_id(&self, data: &T) -> Option<I> {
		return self.data_to_id.get(data).copied();
	}

	pub fn get_data(&self, id: I) -> Option<T> {
		return self.id_to_data.get(&id).cloned();
	}
	
}

impl<T, I> Default for IdCache<T, I>
where
	T: Eq + Hash + Clone,
	I: NumericDatabseId + Eq + Hash + Clone + Copy
{
	fn default() -> Self {
		Self::new()
	}
}

impl UrlCache {
	pub fn get_url(&self, id: UrlId) -> Option<UrlWithoutFragment> {
		self.get_data(id)
	}
}

impl EntityGenerationUuidCache {
	pub fn get_uuid(&self, id: EntityGenerationId) -> Option<Uuid> {
		self.get_data(id)
	}
}