use std::collections::{HashMap, VecDeque};
use std::hash::Hash;
use std::marker::PhantomData;
use unicode_width::UnicodeWidthChar;
pub trait Hasher {
fn hash_key(&self) -> String;
}
#[derive(Debug, Clone)]
struct CacheEntry<T> {
value: T,
access_order: usize,
}
#[derive(Debug)]
pub struct MemoCache<H, T>
where
H: Hasher + Clone,
T: Clone,
{
capacity: usize,
cache: HashMap<String, CacheEntry<T>>,
access_counter: usize,
eviction_queue: VecDeque<String>,
_phantom: PhantomData<H>,
}
impl<H, T> MemoCache<H, T>
where
H: Hasher + Clone,
T: Clone,
{
pub fn new(capacity: usize) -> Self {
Self {
capacity,
cache: HashMap::new(),
access_counter: 0,
eviction_queue: VecDeque::new(),
_phantom: PhantomData,
}
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn size(&self) -> usize {
self.cache.len()
}
pub fn get(&mut self, hashable: &H) -> Option<T> {
let key = hashable.hash_key();
if let Some(entry) = self.cache.get_mut(&key) {
self.access_counter += 1;
entry.access_order = self.access_counter;
if let Some(pos) = self.eviction_queue.iter().position(|x| x == &key) {
self.eviction_queue.remove(pos);
}
self.eviction_queue.push_front(key);
Some(entry.value.clone())
} else {
None
}
}
pub fn set(&mut self, hashable: &H, value: T) {
let key = hashable.hash_key();
if self.cache.contains_key(&key) {
self.access_counter += 1;
self.cache.insert(
key.clone(),
CacheEntry {
value,
access_order: self.access_counter,
},
);
if let Some(pos) = self.eviction_queue.iter().position(|x| x == &key) {
self.eviction_queue.remove(pos);
}
self.eviction_queue.push_front(key);
return;
}
if self.cache.len() >= self.capacity {
self.evict_lru();
}
self.access_counter += 1;
self.cache.insert(
key.clone(),
CacheEntry {
value,
access_order: self.access_counter,
},
);
self.eviction_queue.push_front(key);
}
fn evict_lru(&mut self) {
if let Some(lru_key) = self.eviction_queue.pop_back() {
self.cache.remove(&lru_key);
}
}
pub fn clear(&mut self) {
self.cache.clear();
self.eviction_queue.clear();
self.access_counter = 0;
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Line {
pub runes: Vec<char>,
pub width: usize,
}
impl Hasher for Line {
fn hash_key(&self) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher as StdHasher};
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
format!("{:x}", hasher.finish())
}
}
#[derive(Debug)]
pub struct MemoizedWrap {
cache: HashMap<String, Vec<Vec<char>>>,
}
impl MemoizedWrap {
pub fn new() -> Self {
Self::with_capacity(10000) }
pub fn with_capacity(_capacity: usize) -> Self {
Self {
cache: HashMap::new(),
}
}
pub fn wrap(&mut self, runes: &[char], width: usize) -> Vec<Vec<char>> {
let line = Line {
runes: runes.to_vec(),
width,
};
let key = line.hash_key();
if let Some(cached) = self.cache.get(&key) {
return cached.clone();
}
let wrapped = self.do_wrap(runes, width);
self.cache.insert(key, wrapped.clone());
wrapped
}
fn do_wrap(&self, runes: &[char], width: usize) -> Vec<Vec<char>> {
let mut lines = vec![Vec::new()];
let mut word = Vec::new();
let mut row = 0;
let mut spaces = 0;
for &r in runes {
if r.is_whitespace() {
spaces += 1;
} else {
word.push(r);
}
if spaces > 0 {
let current_line_width = self.line_width(&lines[row]);
let word_width = self.line_width(&word);
if current_line_width + word_width + spaces > width {
row += 1;
lines.push(Vec::new());
lines[row].extend_from_slice(&word);
lines[row].extend(std::iter::repeat_n(' ', spaces));
spaces = 0;
word.clear();
} else {
lines[row].extend_from_slice(&word);
lines[row].extend(std::iter::repeat_n(' ', spaces));
spaces = 0;
word.clear();
}
} else {
let last_char_width = word
.last()
.map(|&ch| UnicodeWidthChar::width(ch).unwrap_or(0))
.unwrap_or(0);
let word_width = self.line_width(&word);
if word_width + last_char_width > width {
if !lines[row].is_empty() {
row += 1;
lines.push(Vec::new());
}
lines[row].extend_from_slice(&word);
word.clear();
}
}
}
let current_line_width = self.line_width(&lines[row]);
let word_width = self.line_width(&word);
if current_line_width + word_width + spaces >= width {
lines.push(Vec::new());
lines[row + 1].extend_from_slice(&word);
spaces += 1;
lines[row + 1].extend(std::iter::repeat_n(' ', spaces));
} else {
lines[row].extend_from_slice(&word);
spaces += 1;
lines[row].extend(std::iter::repeat_n(' ', spaces));
}
lines
}
fn line_width(&self, line: &[char]) -> usize {
line.iter()
.map(|&ch| UnicodeWidthChar::width(ch).unwrap_or(0))
.sum()
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
pub fn capacity(&self) -> usize {
10000 }
pub fn size(&self) -> usize {
self.cache.len()
}
}
impl Default for MemoizedWrap {
fn default() -> Self {
Self::new()
}
}
impl Clone for MemoizedWrap {
fn clone(&self) -> Self {
Self::with_capacity(self.capacity())
}
}