use std::collections::HashMap;
use std::hash::Hash;
use std::rc::Rc;
pub type StringHistoryList = HistoryList<Rc<str>>;
struct Node<T> {
value: T,
prev: Option<usize>,
next: Option<usize>,
}
pub struct HistoryList<T> {
nodes: Vec<Option<Node<T>>>, free_slots: Vec<usize>, index: HashMap<T, usize>, head: Option<usize>,
tail: Option<usize>,
len: usize,
}
impl<T: Eq + Hash + Clone> HistoryList<T> {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
free_slots: Vec::new(),
index: HashMap::new(),
head: None,
tail: None,
len: 0,
}
}
pub fn push_front(&mut self, value: T) {
if let Some(&slot) = self.index.get(&value) {
self.detach(slot);
self.attach_head(slot);
} else {
let slot = self.alloc(value.clone());
self.index.insert(value, slot);
self.attach_head(slot);
self.len += 1;
}
}
pub fn remove(&mut self, value: &T) -> bool {
if let Some(slot) = self.index.remove(value) {
self.detach(slot);
self.free(slot);
self.len -= 1;
true
} else {
false
}
}
pub fn contains(&self, value: &T) -> bool {
self.index.contains_key(value)
}
pub fn get(&self, value: &T) -> Option<&T> {
let &slot = self.index.get(value)?;
self.nodes[slot].as_ref().map(|n| &n.value)
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn iter(&self) -> Iter<'_, T> {
Iter {
nodes: &self.nodes,
current: self.head,
}
}
fn alloc(&mut self, value: T) -> usize {
let node = Node {
value,
prev: None,
next: None,
};
if let Some(slot) = self.free_slots.pop() {
self.nodes[slot] = Some(node);
slot
} else {
self.nodes.push(Some(node));
self.nodes.len() - 1
}
}
fn free(&mut self, slot: usize) {
self.nodes[slot] = None;
self.free_slots.push(slot);
}
fn detach(&mut self, slot: usize) {
let (prev, next) = {
let n = self.nodes[slot].as_ref().unwrap();
(n.prev, n.next)
};
match prev {
Some(p) => self.nodes[p].as_mut().unwrap().next = next,
None => self.head = next, }
match next {
Some(n) => self.nodes[n].as_mut().unwrap().prev = prev,
None => self.tail = prev, }
let n = self.nodes[slot].as_mut().unwrap();
n.prev = None;
n.next = None;
}
fn attach_head(&mut self, slot: usize) {
let old_head = self.head;
if let Some(h) = old_head {
self.nodes[h].as_mut().unwrap().prev = Some(slot);
}
let n = self.nodes[slot].as_mut().unwrap();
n.next = old_head;
n.prev = None;
self.head = Some(slot);
if self.tail.is_none() {
self.tail = Some(slot); }
}
}
impl HistoryList<Rc<str>> {
pub fn push_str(&mut self, s: &str) {
if let Some(&slot) = self.index.get(s) {
self.detach(slot);
self.attach_head(slot);
} else {
let rc: Rc<str> = Rc::from(s); let slot = self.alloc(rc.clone()); self.index.insert(rc, slot);
self.attach_head(slot);
self.len += 1;
}
}
pub fn remove_str(&mut self, s: &str) -> bool {
if let Some(slot) = self.index.remove(s) {
self.detach(slot);
self.free(slot);
self.len -= 1;
true
} else {
false
}
}
pub fn contains_str(&self, s: &str) -> bool {
self.index.contains_key(s)
}
pub fn get_str(&self, s: &str) -> Option<&str> {
let &slot = self.index.get(s)?;
self.nodes[slot].as_ref().map(|n| n.value.as_ref())
}
pub fn str_iter(&self) -> impl Iterator<Item = &str> {
self.iter().map(|rc| rc.as_ref())
}
}
impl<T: Eq + Hash + Clone> FromIterator<T> for HistoryList<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut list = HistoryList::new();
for item in iter {
list.push_front(item);
}
list
}
}
impl<'a> FromIterator<&'a str> for StringHistoryList {
fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
let mut list = StringHistoryList::new();
for s in iter {
list.push_str(s);
}
list
}
}
impl FromIterator<String> for StringHistoryList {
fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
let mut list = StringHistoryList::new();
for s in iter {
list.push_str(&s);
}
list
}
}
impl<T: Eq + Hash + Clone> Default for HistoryList<T> {
fn default() -> Self {
Self::new()
}
}
pub struct Iter<'a, T> {
nodes: &'a [Option<Node<T>>],
current: Option<usize>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let slot = self.current?;
let node = self.nodes[slot].as_ref().unwrap();
self.current = node.next;
Some(&node.value)
}
}
impl std::ops::Index<usize> for StringHistoryList {
type Output = str;
fn index(&self, mut idx: usize) -> &str {
let mut current = self.head;
loop {
let slot = current.expect("StringHistoryList: index out of bounds");
if idx == 0 {
return self.nodes[slot].as_ref().unwrap().value.as_ref();
}
current = self.nodes[slot].as_ref().unwrap().next;
idx -= 1;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn to_strs(h: &StringHistoryList) -> Vec<&str> {
h.str_iter().collect()
}
#[test]
fn index_access() {
let h: StringHistoryList = ["a", "b", "c"].into_iter().collect();
assert_eq!(&h[0], "c");
assert_eq!(&h[1], "b");
assert_eq!(&h[2], "a");
}
#[test]
fn index_after_move_to_front() {
let mut h = StringHistoryList::new();
h.push_str("a");
h.push_str("b");
h.push_str("c");
h.push_str("a"); assert_eq!(&h[0], "a");
assert_eq!(&h[1], "c");
assert_eq!(&h[2], "b");
}
#[test]
fn push_and_order() {
let mut h = StringHistoryList::new();
h.push_str("a");
h.push_str("b");
h.push_str("c");
assert_eq!(to_strs(&h), vec!["c", "b", "a"]);
assert_eq!(h.len(), 3);
}
#[test]
fn duplicate_moves_to_front() {
let mut h = StringHistoryList::new();
h.push_str("a");
h.push_str("b");
h.push_str("c");
h.push_str("a");
assert_eq!(to_strs(&h), vec!["a", "c", "b"]);
assert_eq!(h.len(), 3);
}
#[test]
fn duplicate_of_head_is_noop() {
let mut h = StringHistoryList::new();
h.push_str("a");
h.push_str("b");
h.push_str("b");
assert_eq!(to_strs(&h), vec!["b", "a"]);
assert_eq!(h.len(), 2);
}
#[test]
fn remove_str() {
let mut h = StringHistoryList::new();
h.push_str("a");
h.push_str("b");
h.push_str("c");
assert!(h.remove_str("b"));
assert_eq!(to_strs(&h), vec!["c", "a"]);
assert_eq!(h.len(), 2);
assert!(!h.remove_str("b"));
}
#[test]
fn remove_head_and_tail() {
let mut h = StringHistoryList::new();
h.push_str("a");
h.push_str("b");
h.push_str("c");
h.remove_str("c");
assert_eq!(to_strs(&h), vec!["b", "a"]);
h.remove_str("a");
assert_eq!(to_strs(&h), vec!["b"]);
h.remove_str("b");
assert!(h.is_empty());
}
#[test]
fn contains_and_get_str() {
let mut h = StringHistoryList::new();
h.push_str("hello");
assert!(h.contains_str("hello"));
assert!(!h.contains_str("world"));
assert_eq!(h.get_str("hello"), Some("hello"));
assert_eq!(h.get_str("world"), None);
}
#[test]
fn from_iter_str_slices() {
let h: StringHistoryList = ["a", "b", "c"].into_iter().collect();
assert_eq!(to_strs(&h), vec!["c", "b", "a"]);
}
#[test]
fn from_iter_strings() {
let input = vec!["x".to_string(), "y".to_string(), "z".to_string()];
let h: StringHistoryList = input.into_iter().collect();
assert_eq!(to_strs(&h), vec!["z", "y", "x"]);
}
#[test]
fn from_iter_deduplicates() {
let h: StringHistoryList = ["a", "b", "c", "b"].into_iter().collect();
assert_eq!(to_strs(&h), vec!["b", "c", "a"]);
assert_eq!(h.len(), 3);
}
#[test]
fn from_iter_empty() {
let h: StringHistoryList = std::iter::empty::<&str>().collect();
assert!(h.is_empty());
}
#[test]
fn from_iter_all_duplicates() {
let h: StringHistoryList = ["x", "x", "x"].into_iter().collect();
assert_eq!(to_strs(&h), vec!["x"]);
assert_eq!(h.len(), 1);
}
#[test]
fn slot_reuse() {
let mut h = StringHistoryList::new();
h.push_str("a");
h.push_str("b");
h.remove_str("a");
h.push_str("c");
assert_eq!(to_strs(&h), vec!["c", "b"]);
assert_eq!(h.nodes.len(), 2);
}
#[test]
fn rc_reuse_on_move_to_front() {
let mut h = StringHistoryList::new();
h.push_str("shared");
let rc_before = h.iter().next().unwrap().clone();
h.push_str("shared"); let rc_after = h.iter().next().unwrap().clone();
assert!(Rc::ptr_eq(&rc_before, &rc_after));
}
}