use std::borrow::Borrow;
use camino::{Utf8Path, Utf8PathBuf};
const KANI_IR_HASHMAP_CAPACITY: usize = 4;
#[derive(Debug, Clone, PartialEq)]
pub struct IrHashMap<K, V> {
entries: [Option<(K, V)>; KANI_IR_HASHMAP_CAPACITY],
len: usize,
}
pub trait IrMapKeyEq {
fn ir_map_key_eq(&self, other: &Self) -> bool;
}
impl IrMapKeyEq for String {
fn ir_map_key_eq(&self, other: &Self) -> bool {
self == other
}
}
impl IrMapKeyEq for Utf8PathBuf {
fn ir_map_key_eq(&self, other: &Self) -> bool {
bounded_path_key_eq(self, other)
}
}
impl IrMapKeyEq for &Utf8Path {
fn ir_map_key_eq(&self, other: &Self) -> bool {
bounded_path_eq(self, other)
}
}
fn bounded_path_key_eq(left: &Utf8PathBuf, right: &Utf8PathBuf) -> bool {
bounded_path_eq(left.as_path(), right.as_path())
}
fn bounded_path_eq(left: &Utf8Path, right: &Utf8Path) -> bool {
let left = left.as_str().as_bytes();
let right = right.as_str().as_bytes();
left.len() == 1 && right.len() == 1 && left[0] == right[0]
}
impl<K, V> Default for IrHashMap<K, V> {
fn default() -> Self {
Self {
entries: [None, None, None, None],
len: 0,
}
}
}
impl<K, V> IrHashMap<K, V>
where
K: IrMapKeyEq,
{
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
let mut index = 0;
while index < self.len {
if let Some((candidate, stored)) = &mut self.entries[index] {
if candidate.ir_map_key_eq(&key) {
return Some(std::mem::replace(stored, value));
}
}
index += 1;
}
assert!(
self.len < KANI_IR_HASHMAP_CAPACITY,
"Kani IR map capacity exceeded",
);
self.entries[self.len] = Some((key, value));
self.len += 1;
None
}
}
impl<K, V> IrHashMap<K, V> {
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: PartialEq + ?Sized,
{
let mut index = 0;
while index < self.len {
if let Some((candidate, value)) = &self.entries[index] {
if candidate.borrow() == key {
return Some(value);
}
}
index += 1;
}
None
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: PartialEq + ?Sized,
{
self.get(key).is_some()
}
pub fn keys(&self) -> impl Iterator<Item = &K> {
self.entries[..self.len]
.iter()
.filter_map(|entry| entry.as_ref().map(|(key, _)| key))
}
pub fn key_at(&self, index: usize) -> Option<&K> {
self.entry_at(index).map(|(key, _)| key)
}
pub fn entry_at(&self, index: usize) -> Option<(&K, &V)> {
if index < self.len {
match &self.entries[index] {
Some((key, value)) => Some((key, value)),
None => None,
}
} else {
None
}
}
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
self.entries[..self.len]
.iter()
.filter_map(|entry| entry.as_ref().map(|(key, value)| (key, value)))
}
pub fn values(&self) -> impl Iterator<Item = &V> {
self.entries[..self.len]
.iter()
.filter_map(|entry| entry.as_ref().map(|(_, value)| value))
}
pub fn clear(&mut self) {
let mut index = 0;
while index < self.len {
self.entries[index] = None;
index += 1;
}
self.len = 0;
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}