use crate::splitmix::mix;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::iter::FusedIterator;
use std::sync::Arc;
pub struct History<T>(Option<Arc<Node<T>>>);
struct Node<T> {
value: T,
len: usize,
digest: u64,
rest: History<T>,
}
impl<T> History<T> {
#[must_use]
pub const fn new() -> Self {
History(None)
}
#[must_use]
pub fn len(&self) -> usize {
self.0.as_ref().map_or(0, |node| node.len)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_none()
}
#[must_use]
pub fn head(&self) -> Option<&T> {
self.0.as_ref().map(|node| &node.value)
}
#[must_use]
fn digest(&self) -> u64 {
self.0.as_ref().map_or(0, |node| node.digest)
}
#[must_use]
pub fn push(&self, value: T) -> History<T>
where
T: Hash,
{
History(Some(Arc::new(Node {
len: self.len() + 1,
digest: mix(self.digest(), &value),
value,
rest: self.clone(),
})))
}
#[must_use]
pub fn iter(&self) -> Iter<'_, T> {
Iter(self.0.as_deref())
}
}
impl<T> Clone for History<T> {
fn clone(&self) -> Self {
History(self.0.clone())
}
}
impl<T> Drop for History<T> {
fn drop(&mut self) {
let mut current = self.0.take();
while let Some(node) = current {
match Arc::try_unwrap(node) {
Ok(mut node) => current = node.rest.0.take(),
Err(_) => break,
}
}
}
}
impl<T> Default for History<T> {
fn default() -> Self {
History::new()
}
}
impl<T> Hash for History<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.digest());
}
}
impl<T: PartialEq> PartialEq for History<T> {
fn eq(&self, other: &History<T>) -> bool {
let mut left = &self.0;
let mut right = &other.0;
loop {
match (left, right) {
(None, None) => return true,
(Some(left_node), Some(right_node)) => {
if Arc::ptr_eq(left_node, right_node) {
return true;
}
if left_node.digest != right_node.digest
|| left_node.len != right_node.len
|| left_node.value != right_node.value
{
return false;
}
left = &left_node.rest.0;
right = &right_node.rest.0;
}
_ => return false,
}
}
}
}
impl<T: Eq> Eq for History<T> {}
impl<T: Debug> Debug for History<T> {
fn fmt(&self, out: &mut Formatter<'_>) -> fmt::Result {
out.debug_list().entries(self.iter()).finish()
}
}
impl<'a, T> IntoIterator for &'a History<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Iter<'a, T> {
self.iter()
}
}
pub struct Iter<'a, T>(Option<&'a Node<T>>);
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
let node = self.0?;
self.0 = node.rest.0.as_deref();
Some(&node.value)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.map_or(0, |node| node.len);
(len, Some(len))
}
}
impl<T> ExactSizeIterator for Iter<'_, T> {}
impl<T> FusedIterator for Iter<'_, T> {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Digest<const N: usize = 1>([u64; N]);
impl<const N: usize> Digest<N> {
#[must_use]
pub fn new() -> Self {
Digest([0; N])
}
#[must_use]
pub fn push(self, value: &impl Hash) -> Digest<N> {
let mut lanes = self.0;
for (index, lane) in lanes.iter_mut().enumerate() {
*lane = mix(*lane, &(index, value));
}
Digest(lanes)
}
}
impl<const N: usize> Default for Digest<N> {
fn default() -> Self {
Digest::new()
}
}
#[cfg(test)]
mod tests {
use super::{Digest, History};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn hash_of(value: &impl Hash) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
#[test]
fn history_push_is_structural() {
let base = History::new().push('a').push('b');
let left = base.push('c');
let right = base.push('c');
assert_eq!(left, right);
assert_eq!(hash_of(&left), hash_of(&right));
assert_eq!(left.len(), 3);
assert_eq!(left.head(), Some(&'c'));
assert_ne!(left, base.push('d'));
assert_ne!(left, base);
}
#[test]
fn history_clone_shares_tail() {
let history = History::new().push(1).push(2);
let clone = history.clone();
assert_eq!(history, clone);
assert_eq!(history.iter().copied().collect::<Vec<_>>(), vec![2, 1]);
}
#[test]
fn deep_history_drops_and_compares_without_overflow() {
const DEPTH: u64 = 500_000;
let deep = |extra: u64| {
let mut history = History::new();
for value in 0..DEPTH + extra {
history = history.push(value);
}
history
};
let left = deep(0);
let right = deep(0); assert_eq!(left, right);
assert_ne!(left, deep(1)); drop(left);
drop(right);
}
#[test]
fn empty_history_is_default() {
let empty: History<u8> = History::new();
assert!(empty.is_empty());
assert_eq!(empty, History::default());
assert_eq!(empty.head(), None);
assert_eq!(empty.iter().count(), 0);
}
#[test]
fn digest_matches_equal_histories() {
let left = Digest::<1>::new().push(&1).push(&2);
let right = Digest::<1>::new().push(&1).push(&2);
assert_eq!(left, right);
assert_ne!(left, Digest::<1>::new().push(&2).push(&1));
assert_ne!(left, Digest::<1>::new());
}
#[test]
fn digest_width_is_parametric() {
let wide: Digest<2> = Digest::new().push(&1).push(&2);
assert_eq!(wide, Digest::<2>::new().push(&1).push(&2));
assert_ne!(wide, Digest::<2>::new().push(&2).push(&1));
let [low, high] = wide.0; assert_ne!(low, high, "lanes should not be identical");
}
}