use ahash::AHasher;
use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Index;
use std::slice;
use std::vec;
#[derive(Clone)]
pub struct FrozenMap<K, V> {
entries: Box<[(K, V)]>,
hash: u64,
}
impl<K, V> FrozenMap<K, V>
where
K: Ord + Hash,
V: Hash,
{
pub fn new(entries: impl IntoIterator<Item = (K, V)>) -> Self {
let mut map: BTreeMap<K, V> = BTreeMap::new();
for (k, v) in entries {
map.insert(k, v);
}
let pairs: Vec<(K, V)> = map.into_iter().collect();
let hash = Self::compute_hash(&pairs);
Self {
entries: pairs.into_boxed_slice(),
hash,
}
}
pub fn fromkeys(keys: impl IntoIterator<Item = K>, value: V) -> Self
where
V: Clone,
{
Self::new(keys.into_iter().map(|k| (k, value.clone())))
}
#[inline]
pub fn len(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[inline]
pub fn precomputed_hash(&self) -> u64 {
self.hash
}
#[inline]
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
self.entries
.binary_search_by(|(k, _)| k.borrow().cmp(key))
.ok()
.map(|i| &self.entries[i].1)
}
#[inline]
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
self.get(key).is_some()
}
#[inline]
pub fn keys(&self) -> impl Iterator<Item = &K> {
self.entries.iter().map(|(k, _)| k)
}
#[inline]
pub fn values(&self) -> impl Iterator<Item = &V> {
self.entries.iter().map(|(_, v)| v)
}
#[inline]
pub fn items(&self) -> impl Iterator<Item = (&K, &V)> {
self.entries.iter().map(|(k, v)| (k, v))
}
pub fn pretty_repr(&self, num_spaces: usize) -> String
where
K: fmt::Debug,
V: fmt::Debug,
{
let indent = " ".repeat(num_spaces);
let mut out = String::from("frozendict({\n");
for (k, v) in self.entries.iter() {
out.push_str(&format!("{indent}{k:?}: {v:?},\n"));
}
out.push_str("})");
out
}
fn compute_hash(pairs: &[(K, V)]) -> u64 {
let mut combined: u64 = 0;
for (k, v) in pairs {
let mut hasher = AHasher::default();
k.hash(&mut hasher);
v.hash(&mut hasher);
combined ^= hasher.finish();
}
combined
}
}
impl<K, V> Default for FrozenMap<K, V>
where
K: Ord + Hash,
V: Hash,
{
fn default() -> Self {
Self {
entries: Box::new([]),
hash: 0,
}
}
}
impl<K, V> Hash for FrozenMap<K, V> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash);
}
}
impl<K, V> PartialEq for FrozenMap<K, V>
where
K: PartialEq,
V: PartialEq,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.entries == other.entries
}
}
impl<K, V> Eq for FrozenMap<K, V>
where
K: Eq,
V: Eq,
{
}
impl<K, V> fmt::Debug for FrozenMap<K, V>
where
K: fmt::Debug,
V: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "frozendict({{")?;
for (i, (k, v)) in self.entries.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{k:?}: {v:?}")?;
}
write!(f, "}})")
}
}
impl<K, V> fmt::Display for FrozenMap<K, V>
where
K: fmt::Display,
V: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "frozendict({{")?;
for (i, (k, v)) in self.entries.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{k}: {v}")?;
}
write!(f, "}})")
}
}
impl<K, V, Q> Index<&Q> for FrozenMap<K, V>
where
K: Borrow<Q> + Ord + Hash,
Q: Ord + ?Sized,
V: Hash,
{
type Output = V;
#[inline]
fn index(&self, key: &Q) -> &V {
self.get(key).expect("key not found in FrozenMap")
}
}
impl<K, V> IntoIterator for FrozenMap<K, V> {
type Item = (K, V);
type IntoIter = vec::IntoIter<(K, V)>;
fn into_iter(self) -> Self::IntoIter {
Vec::from(self.entries).into_iter()
}
}
impl<'a, K, V> IntoIterator for &'a FrozenMap<K, V> {
type Item = &'a (K, V);
type IntoIter = slice::Iter<'a, (K, V)>;
fn into_iter(self) -> Self::IntoIter {
self.entries.iter()
}
}
impl<K, V> FromIterator<(K, V)> for FrozenMap<K, V>
where
K: Ord + Hash,
V: Hash,
{
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
Self::new(iter)
}
}