use crate::keymap::full_hash;
use std::iter::FusedIterator;
const LINEAR_MAX: usize = 8;
const MIN_BUCKETS: usize = 16;
#[inline]
const fn load_limit(buckets: usize) -> usize {
buckets / 4 * 3
}
fn buckets_for(entries: usize) -> usize {
let mut buckets = MIN_BUCKETS;
while entries > load_limit(buckets) {
buckets *= 2;
}
buckets
}
const HASH_SEED: u64 = 0xA076_1D64_78BD_642F;
const MAX_INDEXED: usize = u32::MAX as usize;
#[inline]
fn hash_key(key: &str) -> u32 {
let bytes = key.as_bytes();
(full_hash(bytes, bytes.len(), HASH_SEED) >> 32) as u32
}
const EMPTY: u32 = u32::MAX;
#[derive(Clone, Copy)]
struct Bucket {
index: u32,
hash: u32,
}
impl Bucket {
const VACANT: Bucket = Bucket {
index: EMPTY,
hash: 0,
};
#[inline]
fn is_empty(self) -> bool {
self.index == EMPTY
}
}
#[derive(Clone)]
struct HashTable {
buckets: Vec<Bucket>,
}
impl HashTable {
fn build<V>(entries: &[(String, V)]) -> HashTable {
let mut table = HashTable {
buckets: Vec::new(),
};
table.rebuild(entries);
table
}
#[inline]
fn mask(&self) -> u32 {
debug_assert!(!self.buckets.is_empty());
(self.buckets.len() - 1) as u32
}
#[inline]
fn ideal(&self, hash: u32) -> u32 {
hash & self.mask()
}
#[inline]
fn distance(&self, at: u32, hash: u32) -> u32 {
at.wrapping_sub(self.ideal(hash)) & self.mask()
}
#[inline]
fn next(&self, at: u32) -> u32 {
(at + 1) & self.mask()
}
fn find<V>(&self, entries: &[(String, V)], key: &str, hash: u32) -> Option<usize> {
let mut at = self.ideal(hash);
let mut travelled = 0u32;
loop {
let bucket = self.buckets[at as usize];
if bucket.is_empty() || self.distance(at, bucket.hash) < travelled {
return None;
}
if bucket.hash == hash {
let pos = bucket.index as usize;
if entries[pos].0.as_str() == key {
return Some(pos);
}
}
at = self.next(at);
travelled += 1;
}
}
fn place(&mut self, entry: Bucket) {
debug_assert!(!entry.is_empty());
let mut carried = entry;
let mut at = self.ideal(carried.hash);
let mut travelled = 0u32;
loop {
let occupant = self.buckets[at as usize];
if occupant.is_empty() {
self.buckets[at as usize] = carried;
return;
}
let theirs = self.distance(at, occupant.hash);
if theirs < travelled {
self.buckets[at as usize] = carried;
carried = occupant;
travelled = theirs;
}
at = self.next(at);
travelled += 1;
}
}
fn bucket_of<V>(&self, entries: &[(String, V)], pos: u32) -> u32 {
let hash = hash_key(&entries[pos as usize].0);
let mut at = self.ideal(hash);
for _ in 0..self.buckets.len() {
if self.buckets[at as usize].index == pos {
return at;
}
at = self.next(at);
}
unreachable!("structio: OrderedMap entry {pos} has no bucket");
}
fn erase(&mut self, at: u32) {
let mut hole = at;
let mut curr = self.next(at);
loop {
let bucket = self.buckets[curr as usize];
if bucket.is_empty() || self.distance(curr, bucket.hash) == 0 {
self.buckets[hole as usize] = Bucket::VACANT;
return;
}
self.buckets[hole as usize] = bucket;
hole = curr;
curr = self.next(curr);
}
}
fn shift_down_above(&mut self, pos: u32) {
for bucket in &mut self.buckets {
if !bucket.is_empty() && bucket.index > pos {
bucket.index -= 1;
}
}
}
fn rebuild<V>(&mut self, entries: &[(String, V)]) {
debug_assert!(entries.len() <= MAX_INDEXED);
self.buckets.clear();
self.buckets
.resize(buckets_for(entries.len()), Bucket::VACANT);
for (pos, (key, _)) in entries.iter().enumerate() {
self.place(Bucket {
index: pos as u32,
hash: hash_key(key),
});
}
}
fn grow(&mut self) {
let doubled = self.buckets.len() * 2;
let old = std::mem::replace(&mut self.buckets, vec![Bucket::VACANT; doubled]);
for bucket in old {
if !bucket.is_empty() {
self.place(bucket);
}
}
}
}
enum Lookup {
Occupied(usize),
Vacant(Option<u32>),
}
pub struct OrderedMap<V> {
entries: Vec<(String, V)>,
table: Option<Box<HashTable>>,
}
impl<V> OrderedMap<V> {
#[inline]
pub fn new() -> Self {
OrderedMap {
entries: Vec::new(),
table: None,
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
OrderedMap {
entries: Vec::with_capacity(capacity),
table: None,
}
}
#[inline]
pub fn capacity(&self) -> usize {
self.entries.capacity()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.entries.reserve(additional);
}
#[inline]
pub fn len(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.entries.clear();
self.table = None;
}
#[inline]
fn locate(&self, key: &str) -> Lookup {
let Some(table) = self.table.as_deref() else {
return match self.entries.iter().position(|(k, _)| k.as_str() == key) {
Some(pos) => Lookup::Occupied(pos),
None => Lookup::Vacant(None),
};
};
let hash = hash_key(key);
match table.find(&self.entries, key, hash) {
Some(pos) => Lookup::Occupied(pos),
None => Lookup::Vacant(Some(hash)),
}
}
#[inline]
fn find(&self, key: &str) -> Option<usize> {
match self.locate(key) {
Lookup::Occupied(pos) => Some(pos),
Lookup::Vacant(_) => None,
}
}
#[inline]
pub fn get(&self, key: &str) -> Option<&V> {
self.find(key).map(|pos| &self.entries[pos].1)
}
#[inline]
pub fn get_mut(&mut self, key: &str) -> Option<&mut V> {
self.find(key).map(|pos| &mut self.entries[pos].1)
}
#[inline]
pub fn get_key_value(&self, key: &str) -> Option<(&str, &V)> {
self.find(key).map(|pos| {
let (k, v) = &self.entries[pos];
(k.as_str(), v)
})
}
#[inline]
pub fn contains_key(&self, key: &str) -> bool {
self.find(key).is_some()
}
pub fn insert(&mut self, key: String, value: V) -> Option<V> {
match self.locate(&key) {
Lookup::Occupied(pos) => Some(std::mem::replace(&mut self.entries[pos].1, value)),
Lookup::Vacant(hash) => {
self.push_new(key, value, hash);
None
}
}
}
fn push_new(&mut self, key: String, value: V, hash: Option<u32>) -> usize {
let pos = self.entries.len();
self.entries.push((key, value));
let len = self.entries.len();
if len <= LINEAR_MAX {
debug_assert!(self.table.is_none());
} else if len > MAX_INDEXED {
self.table = None;
} else if let Some(table) = self.table.as_deref_mut() {
if len > load_limit(table.buckets.len()) {
table.grow();
}
let hash = hash.unwrap_or_else(|| hash_key(&self.entries[pos].0));
table.place(Bucket {
index: pos as u32,
hash,
});
} else {
self.reindex();
}
pos
}
#[inline]
pub fn remove(&mut self, key: &str) -> Option<V> {
self.remove_entry(key).map(|(_, value)| value)
}
pub fn remove_entry(&mut self, key: &str) -> Option<(String, V)> {
let pos = self.find(key)?;
Some(self.remove_at(pos))
}
fn remove_at(&mut self, pos: usize) -> (String, V) {
if self.entries.len() - 1 > LINEAR_MAX
&& let Some(table) = self.table.as_deref_mut()
{
let bucket = table.bucket_of(&self.entries, pos as u32);
table.erase(bucket);
let removed = self.entries.remove(pos);
table.shift_down_above(pos as u32);
return removed;
}
let removed = self.entries.remove(pos);
self.reindex();
removed
}
fn reindex(&mut self) {
let len = self.entries.len();
if len <= LINEAR_MAX || len > MAX_INDEXED {
self.table = None;
} else if let Some(table) = self.table.as_deref_mut() {
table.rebuild(&self.entries);
} else {
self.table = Some(Box::new(HashTable::build(&self.entries)));
}
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&str, &mut V) -> bool,
{
let before = self.entries.len();
self.entries
.retain_mut(|(key, value)| f(key.as_str(), value));
if self.entries.len() != before {
self.reindex();
}
}
pub fn sort_keys(&mut self) {
self.entries.sort_unstable_by(|a, b| a.0.cmp(&b.0));
self.reindex();
}
#[inline]
pub fn get_index(&self, index: usize) -> Option<(&str, &V)> {
self.entries.get(index).map(|(k, v)| (k.as_str(), v))
}
#[inline]
pub fn get_index_mut(&mut self, index: usize) -> Option<(&str, &mut V)> {
self.entries
.get_mut(index)
.map(|(k, v)| (k.as_str(), &mut *v))
}
#[inline]
pub fn first(&self) -> Option<(&str, &V)> {
self.get_index(0)
}
#[inline]
pub fn last(&self) -> Option<(&str, &V)> {
self.entries.last().map(|(k, v)| (k.as_str(), v))
}
pub fn entry(&mut self, key: String) -> Entry<'_, V> {
match self.locate(&key) {
Lookup::Occupied(pos) => Entry::Occupied(OccupiedEntry { map: self, pos }),
Lookup::Vacant(hash) => Entry::Vacant(VacantEntry {
map: self,
key,
hash,
}),
}
}
#[inline]
pub fn iter(&self) -> Iter<'_, V> {
Iter {
inner: self.entries.iter(),
}
}
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_, V> {
IterMut {
inner: self.entries.iter_mut(),
}
}
#[inline]
pub fn keys(&self) -> Keys<'_, V> {
Keys {
inner: self.entries.iter(),
}
}
#[inline]
pub fn values(&self) -> Values<'_, V> {
Values {
inner: self.entries.iter(),
}
}
#[inline]
pub fn values_mut(&mut self) -> ValuesMut<'_, V> {
ValuesMut {
inner: self.entries.iter_mut(),
}
}
}
impl<V> Default for OrderedMap<V> {
#[inline]
fn default() -> Self {
OrderedMap::new()
}
}
impl<V: Clone> Clone for OrderedMap<V> {
fn clone(&self) -> Self {
OrderedMap {
entries: self.entries.clone(),
table: self.table.clone(),
}
}
}
impl<V: core::fmt::Debug> core::fmt::Debug for OrderedMap<V> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
impl<V: PartialEq> PartialEq for OrderedMap<V> {
fn eq(&self, other: &Self) -> bool {
self.len() == other.len()
&& self
.iter()
.all(|(key, value)| other.get(key).is_some_and(|theirs| value == theirs))
}
}
impl<V: Eq> Eq for OrderedMap<V> {}
impl<V> core::ops::Index<&str> for OrderedMap<V> {
type Output = V;
#[inline]
fn index(&self, key: &str) -> &V {
self.get(key)
.expect("structio: no entry found for key in OrderedMap")
}
}
impl<V> FromIterator<(String, V)> for OrderedMap<V> {
fn from_iter<I: IntoIterator<Item = (String, V)>>(iter: I) -> Self {
let iter = iter.into_iter();
let mut map = OrderedMap::with_capacity(iter.size_hint().0);
map.extend(iter);
map
}
}
impl<V> Extend<(String, V)> for OrderedMap<V> {
fn extend<I: IntoIterator<Item = (String, V)>>(&mut self, iter: I) {
for (key, value) in iter {
self.insert(key, value);
}
}
}
impl<V, const N: usize> From<[(String, V); N]> for OrderedMap<V> {
fn from(entries: [(String, V); N]) -> Self {
OrderedMap::from_iter(entries)
}
}
pub enum Entry<'a, V> {
Occupied(OccupiedEntry<'a, V>),
Vacant(VacantEntry<'a, V>),
}
impl<'a, V> Entry<'a, V> {
pub fn key(&self) -> &str {
match self {
Entry::Occupied(e) => e.key(),
Entry::Vacant(e) => e.key(),
}
}
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => e.insert(default),
}
}
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
match self {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => e.insert(default()),
}
}
#[must_use]
pub fn and_modify<F: FnOnce(&mut V)>(self, f: F) -> Self {
match self {
Entry::Occupied(mut e) => {
f(e.get_mut());
Entry::Occupied(e)
}
Entry::Vacant(e) => Entry::Vacant(e),
}
}
}
impl<'a, V: Default> Entry<'a, V> {
pub fn or_default(self) -> &'a mut V {
self.or_insert_with(V::default)
}
}
pub struct OccupiedEntry<'a, V> {
map: &'a mut OrderedMap<V>,
pos: usize,
}
impl<'a, V> OccupiedEntry<'a, V> {
#[inline]
pub fn key(&self) -> &str {
self.map.entries[self.pos].0.as_str()
}
#[inline]
pub fn get(&self) -> &V {
&self.map.entries[self.pos].1
}
#[inline]
pub fn get_mut(&mut self) -> &mut V {
&mut self.map.entries[self.pos].1
}
#[inline]
pub fn into_mut(self) -> &'a mut V {
&mut self.map.entries[self.pos].1
}
#[inline]
pub fn insert(&mut self, value: V) -> V {
std::mem::replace(self.get_mut(), value)
}
#[inline]
pub fn remove(self) -> V {
self.remove_entry().1
}
#[inline]
pub fn remove_entry(self) -> (String, V) {
self.map.remove_at(self.pos)
}
}
pub struct VacantEntry<'a, V> {
map: &'a mut OrderedMap<V>,
key: String,
hash: Option<u32>,
}
impl<'a, V> VacantEntry<'a, V> {
#[inline]
pub fn key(&self) -> &str {
self.key.as_str()
}
#[inline]
pub fn into_key(self) -> String {
self.key
}
pub fn insert(self, value: V) -> &'a mut V {
let pos = self.map.push_new(self.key, value, self.hash);
&mut self.map.entries[pos].1
}
}
pub struct Iter<'a, V> {
inner: core::slice::Iter<'a, (String, V)>,
}
impl<V> Clone for Iter<'_, V> {
fn clone(&self) -> Self {
Iter {
inner: self.inner.clone(),
}
}
}
impl<'a, V> Iterator for Iter<'a, V> {
type Item = (&'a String, &'a V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, v)| (k, v))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<V> DoubleEndedIterator for Iter<'_, V> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|(k, v)| (k, v))
}
}
impl<V> ExactSizeIterator for Iter<'_, V> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<V> FusedIterator for Iter<'_, V> {}
pub struct IterMut<'a, V> {
inner: core::slice::IterMut<'a, (String, V)>,
}
impl<'a, V> Iterator for IterMut<'a, V> {
type Item = (&'a String, &'a mut V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, v)| (&*k, v))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<V> DoubleEndedIterator for IterMut<'_, V> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|(k, v)| (&*k, v))
}
}
impl<V> ExactSizeIterator for IterMut<'_, V> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<V> FusedIterator for IterMut<'_, V> {}
pub struct IntoIter<V> {
inner: std::vec::IntoIter<(String, V)>,
}
impl<V> Iterator for IntoIter<V> {
type Item = (String, V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<V> DoubleEndedIterator for IntoIter<V> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}
impl<V> ExactSizeIterator for IntoIter<V> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<V> FusedIterator for IntoIter<V> {}
pub struct Keys<'a, V> {
inner: core::slice::Iter<'a, (String, V)>,
}
impl<V> Clone for Keys<'_, V> {
fn clone(&self) -> Self {
Keys {
inner: self.inner.clone(),
}
}
}
impl<'a, V> Iterator for Keys<'a, V> {
type Item = &'a String;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, _)| k)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<V> DoubleEndedIterator for Keys<'_, V> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|(k, _)| k)
}
}
impl<V> ExactSizeIterator for Keys<'_, V> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<V> FusedIterator for Keys<'_, V> {}
pub struct Values<'a, V> {
inner: core::slice::Iter<'a, (String, V)>,
}
impl<V> Clone for Values<'_, V> {
fn clone(&self) -> Self {
Values {
inner: self.inner.clone(),
}
}
}
impl<'a, V> Iterator for Values<'a, V> {
type Item = &'a V;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(_, v)| v)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<V> DoubleEndedIterator for Values<'_, V> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|(_, v)| v)
}
}
impl<V> ExactSizeIterator for Values<'_, V> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<V> FusedIterator for Values<'_, V> {}
pub struct ValuesMut<'a, V> {
inner: core::slice::IterMut<'a, (String, V)>,
}
impl<'a, V> Iterator for ValuesMut<'a, V> {
type Item = &'a mut V;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(_, v)| v)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<V> DoubleEndedIterator for ValuesMut<'_, V> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|(_, v)| v)
}
}
impl<V> ExactSizeIterator for ValuesMut<'_, V> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<V> FusedIterator for ValuesMut<'_, V> {}
impl<V> IntoIterator for OrderedMap<V> {
type Item = (String, V);
type IntoIter = IntoIter<V>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
inner: self.entries.into_iter(),
}
}
}
impl<'a, V> IntoIterator for &'a OrderedMap<V> {
type Item = (&'a String, &'a V);
type IntoIter = Iter<'a, V>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, V> IntoIterator for &'a mut OrderedMap<V> {
type Item = (&'a String, &'a mut V);
type IntoIter = IterMut<'a, V>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
fn nth_key(n: usize) -> String {
const ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
let mut n = n;
let mut key = String::from("key_");
loop {
key.push(ALPHABET[n % ALPHABET.len()] as char);
n /= ALPHABET.len();
if n == 0 {
break;
}
}
key
}
fn map_of(n: usize) -> OrderedMap<usize> {
let mut map = OrderedMap::new();
for i in 0..n {
map.insert(nth_key(i), i);
}
map
}
fn table_of<V>(map: &OrderedMap<V>) -> &HashTable {
map.table
.as_deref()
.expect("the map is large enough to have a table")
}
fn bucket_count<V>(map: &OrderedMap<V>) -> usize {
map.table.as_deref().map_or(0, |table| table.buckets.len())
}
fn assert_index_consistent<V>(map: &OrderedMap<V>) {
let len = map.len();
let Some(table) = map.table.as_deref() else {
assert!(
len <= LINEAR_MAX || len > MAX_INDEXED,
"a map of {len} entries is missing its table"
);
return;
};
assert!(len > LINEAR_MAX, "a map of {len} entries built a table");
assert!(
!table.buckets.is_empty(),
"a map of {len} entries holds a table with no buckets, which is \
the representation `None` is for"
);
let buckets = table.buckets.len();
assert!(
buckets.is_power_of_two(),
"{buckets} buckets is not a power of two"
);
assert!(
buckets >= MIN_BUCKETS,
"{buckets} buckets is below the minimum"
);
assert!(
len <= load_limit(buckets),
"{len} entries in {buckets} buckets is over the load factor"
);
let mut bucketed = vec![false; len];
for (at, bucket) in table.buckets.iter().enumerate() {
if bucket.is_empty() {
continue;
}
let at = at as u32;
let pos = bucket.index as usize;
assert!(pos < len, "bucket {at} points past the entries");
assert_eq!(
bucket.hash,
hash_key(&map.entries[pos].0),
"bucket {at} holds a stale hash"
);
assert!(
!std::mem::replace(&mut bucketed[pos], true),
"two buckets point at entry {pos}"
);
let ideal = table.ideal(bucket.hash);
for step in 0..table.distance(at, bucket.hash) {
let passed_at = (ideal + step) & table.mask();
let passed = table.buckets[passed_at as usize];
assert!(
!passed.is_empty(),
"entry {pos} is unreachable: bucket {passed_at} is empty"
);
assert!(
table.distance(passed_at, passed.hash) >= step,
"robin hood ordering is broken at bucket {passed_at}"
);
}
}
assert!(
bucketed.iter().all(|&seen| seen),
"an entry has no bucket of its own"
);
for (pos, (key, _)) in map.entries.iter().enumerate() {
assert_eq!(
table.find(&map.entries, key, hash_key(key)),
Some(pos),
"entry {pos} is not where a probe looks for it"
);
}
}
#[test]
fn empty_map_answers_nothing() {
let map: OrderedMap<i32> = OrderedMap::new();
assert!(map.is_empty());
assert_eq!(map.len(), 0);
assert_eq!(map.get("a"), None);
assert!(!map.contains_key("a"));
assert_eq!(map.first(), None);
assert_eq!(map.last(), None);
assert_eq!(map.iter().next(), None);
}
#[test]
fn insertion_order_survives_every_size() {
for n in [0usize, 1, 7, 8, 9, 63, 64, 65, 300, 1000] {
let map = map_of(n);
assert_eq!(map.len(), n);
assert_index_consistent(&map);
let expected: Vec<String> = (0..n).map(nth_key).collect();
let actual: Vec<String> = map.keys().cloned().collect();
assert_eq!(actual, expected, "order wrong at n = {n}");
for i in 0..n {
assert_eq!(map.get(&nth_key(i)), Some(&i), "missing key at n = {n}");
assert_eq!(map.get_index(i), Some((nth_key(i).as_str(), &i)));
}
assert_eq!(map.get("key_not_present"), None, "phantom key at n = {n}");
assert_eq!(map.get(""), None);
}
}
#[test]
fn duplicate_insert_replaces_in_place() {
for n in [1usize, 8, 9, 40, 200] {
let mut map = map_of(n);
let mut targets = vec![0usize, n / 2, n - 1];
targets.dedup();
for target in targets {
let key = nth_key(target);
let old = map.insert(key.clone(), 10_000 + target);
assert_eq!(old, Some(target), "old value not returned at n = {n}");
assert_eq!(map.len(), n, "duplicate insert changed the length");
assert_eq!(map.get(&key), Some(&(10_000 + target)));
assert_eq!(
map.get_index(target).map(|(k, _)| k.to_string()),
Some(key),
"duplicate insert moved the entry"
);
}
assert_index_consistent(&map);
assert_eq!(
bucket_count(&map),
bucket_count(&map_of(n)),
"a duplicate insert grew the table at n = {n}"
);
}
}
fn keys_for_bucket(bucket: u32, buckets: usize, wanted: usize) -> Vec<String> {
let mask = (buckets - 1) as u32;
let mut found = Vec::new();
for i in 0..1_000_000usize {
let key = nth_key(i);
if hash_key(&key) & mask == bucket {
found.push(key);
if found.len() == wanted {
return found;
}
}
}
panic!("fewer than {wanted} keys land in bucket {bucket} of {buckets}");
}
fn bucket_of<V>(map: &OrderedMap<V>, key: &str) -> u32 {
let pos = map.find(key).expect("key is present") as u32;
table_of(map).bucket_of(&map.entries, pos)
}
fn distance_of<V>(map: &OrderedMap<V>, key: &str) -> u32 {
table_of(map).distance(bucket_of(map, key), hash_key(key))
}
#[test]
fn the_table_appears_and_doubles_at_the_right_sizes() {
let mut map: OrderedMap<usize> = OrderedMap::new();
for n in 1..=200usize {
map.insert(nth_key(n - 1), n - 1);
assert_eq!(map.len(), n);
if n <= LINEAR_MAX {
assert!(map.table.is_none(), "a map of {n} entries built a table");
} else {
assert_eq!(
bucket_count(&map),
buckets_for(n),
"wrong table size at n = {n}"
);
}
assert_index_consistent(&map);
for i in 0..n {
assert_eq!(map.get(&nth_key(i)), Some(&i), "lost a key at n = {n}");
}
}
assert_eq!(buckets_for(LINEAR_MAX + 1), MIN_BUCKETS);
for n in 1..=200usize {
let buckets = buckets_for(n);
assert!(n <= load_limit(buckets));
assert!(buckets == MIN_BUCKETS || n > load_limit(buckets / 2));
}
}
#[test]
fn probes_wrap_around_the_end_of_the_table() {
let last = (MIN_BUCKETS - 1) as u32;
let chain = keys_for_bucket(last, MIN_BUCKETS, 4);
let mut map: OrderedMap<String> = OrderedMap::new();
for key in &chain {
map.insert(key.clone(), format!("{key}!"));
}
let mut filler = Vec::new();
while map.len() < load_limit(MIN_BUCKETS) {
let key = nth_key(5_000_000 + filler.len());
map.insert(key.clone(), String::new());
filler.push(key);
}
assert_eq!(bucket_count(&map), MIN_BUCKETS);
assert_index_consistent(&map);
let wrapped = chain.iter().filter(|k| bucket_of(&map, k) < last).count();
assert_eq!(wrapped, chain.len() - 1, "the chain did not wrap");
for key in &chain {
assert_eq!(map.get(key), Some(&format!("{key}!")));
}
assert_eq!(map.get("absent"), None);
let head = chain[0].clone();
assert_eq!(bucket_of(&map, &head), last);
assert_eq!(map.remove(&head), Some(format!("{head}!")));
assert_index_consistent(&map);
assert_eq!(map.get(&head), None);
for key in chain[1..].iter().chain(&filler) {
assert!(map.contains_key(key), "lost {key} to a wrapped removal");
}
}
#[test]
fn a_later_insert_displaces_an_entry_that_is_closer_to_home() {
let mut filler = keys_for_bucket(8, MIN_BUCKETS, 6);
filler.extend(keys_for_bucket(4, MIN_BUCKETS, 2));
let mut map: OrderedMap<u32> = OrderedMap::new();
for (i, key) in filler.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
assert_eq!(map.len(), LINEAR_MAX);
let zero = keys_for_bucket(0, MIN_BUCKETS, 2);
let one = keys_for_bucket(1, MIN_BUCKETS, 1);
map.insert(zero[0].clone(), 100);
map.insert(one[0].clone(), 101);
map.insert(zero[1].clone(), 102);
assert_eq!(bucket_count(&map), MIN_BUCKETS);
assert_eq!(bucket_of(&map, &zero[0]), 0);
assert_eq!(
bucket_of(&map, &zero[1]),
1,
"the later insert did not take the bucket"
);
assert_eq!(
bucket_of(&map, &one[0]),
2,
"the entry that was home was not displaced"
);
assert_index_consistent(&map);
assert_eq!(map.get(&zero[0]), Some(&100));
assert_eq!(map.get(&one[0]), Some(&101));
assert_eq!(map.get(&zero[1]), Some(&102));
for (i, key) in filler.iter().enumerate() {
assert_eq!(map.get(key), Some(&(i as u32)));
}
}
#[test]
fn removing_the_head_of_a_chain_keeps_the_rest_reachable() {
let chain = keys_for_bucket(3, MIN_BUCKETS, 5);
let mut map: OrderedMap<String> = OrderedMap::new();
for key in &chain {
map.insert(key.clone(), format!("{key}!"));
}
let filler = keys_for_bucket(10, MIN_BUCKETS, load_limit(MIN_BUCKETS) - chain.len());
for key in &filler {
map.insert(key.clone(), String::new());
}
assert_index_consistent(&map);
for (step, key) in chain.iter().enumerate() {
assert_eq!(
distance_of(&map, key),
step as u32,
"the keys did not form one chain"
);
}
for removed in [0usize, 1] {
let key = &chain[removed];
assert_eq!(map.remove(key), Some(format!("{key}!")));
assert_index_consistent(&map);
assert_eq!(map.get(key), None);
for survivor in &chain[removed + 1..] {
assert_eq!(
map.get(survivor),
Some(&format!("{survivor}!")),
"removing {key} cut {survivor} out of its chain"
);
}
for key in &filler {
assert_eq!(map.get(key), Some(&String::new()));
}
}
}
#[test]
fn filling_rebuilds_a_bounded_number_of_times() {
const N: usize = 4_000;
let mut map: OrderedMap<usize> = OrderedMap::new();
let mut buckets = 0;
let mut rebuilds = 0usize;
let mut rebuilt_entries = 0usize;
for i in 0..N {
map.insert(nth_key(i), i);
if bucket_count(&map) != buckets {
buckets = bucket_count(&map);
rebuilds += 1;
rebuilt_entries += map.len();
}
}
assert_eq!(map.len(), N);
assert_index_consistent(&map);
assert!(
rebuilds <= (N.ilog2() as usize) + 1,
"{rebuilds} rebuilds filling {N} entries"
);
assert!(
rebuilt_entries <= 2 * N,
"rebuilding walked {rebuilt_entries} entries filling {N}"
);
}
fn colliding_pairs(wanted: usize) -> Vec<(String, String)> {
let mut first_seen: std::collections::HashMap<u32, usize> =
std::collections::HashMap::new();
let mut pairs = Vec::new();
for i in 0..2_000_000usize {
let key = nth_key(i);
let hash = hash_key(&key);
match first_seen.get(&hash) {
Some(&j) => {
pairs.push((nth_key(j), key));
if pairs.len() == wanted {
return pairs;
}
}
None => {
first_seen.insert(hash, i);
}
}
}
panic!("no 32-bit hash collision found; the search bound is too low");
}
#[test]
fn colliding_keys_are_all_found_and_no_others() {
let pairs = colliding_pairs(3);
for (a, b) in &pairs {
assert_ne!(a, b);
assert_eq!(hash_key(a), hash_key(b), "test setup is not a collision");
}
let mut small: OrderedMap<&str> = OrderedMap::new();
let (a, b) = &pairs[0];
small.insert(a.clone(), "a");
assert_eq!(small.get(a), Some(&"a"));
assert_eq!(
small.get(b),
None,
"an absent key with a colliding hash was found"
);
small.insert(b.clone(), "b");
assert_eq!(small.get(a), Some(&"a"));
assert_eq!(small.get(b), Some(&"b"));
let mut big: OrderedMap<String> = OrderedMap::new();
for (a, _) in &pairs {
big.insert(a.clone(), format!("{a}!"));
}
for i in 0..200 {
big.insert(nth_key(1_000_000 + i), i.to_string());
}
assert!(big.table.is_some(), "the hashed path was never exercised");
assert_index_consistent(&big);
for (a, b) in &pairs {
assert_eq!(big.get(a), Some(&format!("{a}!")));
assert_eq!(big.get(b), None, "absent colliding key was found");
}
for (_, b) in &pairs {
big.insert(b.clone(), format!("{b}?"));
}
for _ in 0..20 {
big.insert(nth_key(2_000_000 + big.len()), String::new());
}
assert_index_consistent(&big);
for (a, b) in &pairs {
assert_eq!(big.get(a), Some(&format!("{a}!")));
assert_eq!(big.get(b), Some(&format!("{b}?")));
}
}
#[test]
fn colliding_keys_survive_removal() {
let pairs = colliding_pairs(2);
let mut map: OrderedMap<String> = OrderedMap::new();
for (a, b) in &pairs {
map.insert(a.clone(), format!("{a}!"));
map.insert(b.clone(), format!("{b}?"));
}
for i in 0..100 {
map.insert(nth_key(3_000_000 + i), i.to_string());
}
for (a, b) in &pairs {
assert_eq!(map.remove(a), Some(format!("{a}!")));
assert_eq!(map.get(a), None);
assert_eq!(
map.get(b),
Some(&format!("{b}?")),
"removing one of a colliding pair lost the other"
);
assert_index_consistent(&map);
}
}
#[test]
fn remove_preserves_order() {
for n in [1usize, 8, 9, 40, 128] {
for &target in &[0usize, n / 2, n - 1] {
let mut map = map_of(n);
let key = nth_key(target);
assert_eq!(map.remove(&key), Some(target));
assert_eq!(map.remove(&key), None, "removed twice");
assert_eq!(map.len(), n - 1);
assert_index_consistent(&map);
let expected: Vec<String> = (0..n).filter(|&i| i != target).map(nth_key).collect();
assert_eq!(map.keys().cloned().collect::<Vec<_>>(), expected);
for i in 0..n {
let found = map.get(&nth_key(i));
if i == target {
assert_eq!(found, None, "n = {n}, removed = {target}");
} else {
assert_eq!(found, Some(&i), "n = {n}, removed = {target}");
}
}
}
}
}
#[test]
fn remove_everything_one_at_a_time() {
let mut map = map_of(100);
for i in (0..100).rev() {
assert_eq!(map.remove_entry(&nth_key(i)), Some((nth_key(i), i)));
assert_index_consistent(&map);
assert_eq!(map.len(), i);
for j in 0..i {
assert_eq!(map.get(&nth_key(j)), Some(&j));
}
}
assert!(map.is_empty());
}
#[test]
fn remove_from_the_front_repeatedly() {
let mut map = map_of(60);
for i in 0..60 {
assert_eq!(map.remove(&nth_key(i)), Some(i));
assert_index_consistent(&map);
assert_eq!(map.first().map(|(k, _)| k.to_string()), {
if i + 1 < 60 {
Some(nth_key(i + 1))
} else {
None
}
});
}
}
#[test]
fn crossing_the_linear_threshold_in_both_directions() {
let mut map = map_of(12);
assert!(map.table.is_some());
for removed in 0..5 {
assert_eq!(map.remove(&nth_key(removed)), Some(removed));
assert_index_consistent(&map);
assert_eq!(map.len(), 11 - removed);
for survivor in removed + 1..12 {
assert_eq!(map.get(&nth_key(survivor)), Some(&survivor));
}
}
assert!(
map.table.is_none(),
"the table outlived the map's need for it"
);
assert_eq!(
map.keys().cloned().collect::<Vec<_>>(),
(5..12).map(nth_key).collect::<Vec<_>>()
);
for added in 12..24 {
map.insert(nth_key(added), added);
assert_index_consistent(&map);
}
assert_eq!(map.len(), 19);
assert!(map.table.is_some());
for i in 0..24 {
let found = map.get(&nth_key(i));
assert_eq!(found, if i < 5 { None } else { Some(&i) });
}
assert_eq!(
map.keys().cloned().collect::<Vec<_>>(),
(5..24).map(nth_key).collect::<Vec<_>>()
);
}
#[test]
fn clear_forgets_everything() {
let mut map = map_of(50);
map.clear();
assert!(map.is_empty());
assert_eq!(map.get(&nth_key(0)), None);
assert_index_consistent(&map);
map.insert("a".into(), 1);
assert_eq!(map.get("a"), Some(&1));
assert_eq!(map.len(), 1);
map.clear();
for i in 0..40 {
map.insert(nth_key(i), i);
assert_index_consistent(&map);
}
assert_eq!(map.get("a"), None);
assert_eq!(
map.keys().cloned().collect::<Vec<_>>(),
(0..40).map(nth_key).collect::<Vec<_>>()
);
for i in 0..40 {
assert_eq!(map.get(&nth_key(i)), Some(&i));
}
}
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn below(&mut self, n: u64) -> u64 {
self.next() % n
}
}
#[test]
fn matches_btreemap_over_a_long_script() {
let mut rng = Rng(0x5EED_1234_ABCD_0001);
let mut map: OrderedMap<u64> = OrderedMap::new();
let mut reference: BTreeMap<String, u64> = BTreeMap::new();
let mut order: Vec<String> = Vec::new();
const KEY_SPACE: u64 = 400;
for step in 0..20_000u32 {
let key = nth_key(rng.below(KEY_SPACE) as usize);
match rng.below(100) {
0..=54 => {
let value = rng.next();
let ours = map.insert(key.clone(), value);
let theirs = reference.insert(key.clone(), value);
assert_eq!(ours, theirs, "step {step}: insert returned differently");
if theirs.is_none() {
order.push(key);
}
}
55..=74 => {
assert_eq!(
map.get(&key),
reference.get(&key),
"step {step}: get disagreed"
);
}
75..=84 => {
assert_eq!(
map.contains_key(&key),
reference.contains_key(&key),
"step {step}: contains_key disagreed"
);
}
85..=97 => {
let ours = map.remove(&key);
let theirs = reference.remove(&key);
assert_eq!(ours, theirs, "step {step}: remove returned differently");
if theirs.is_some() {
let at = order.iter().position(|k| *k == key).unwrap();
order.remove(at);
}
}
_ => {
let value = rng.next();
let ours = *map.entry(key.clone()).or_insert(value);
let theirs = *reference.entry(key.clone()).or_insert(value);
assert_eq!(ours, theirs, "step {step}: entry disagreed");
if ours == value && theirs == value && !order.contains(&key) {
order.push(key);
}
}
}
assert_eq!(map.len(), reference.len(), "step {step}: length disagreed");
if step % 97 == 0 {
assert_index_consistent(&map);
let mut ours: Vec<(String, u64)> =
map.iter().map(|(k, v)| (k.clone(), *v)).collect();
ours.sort();
let theirs: Vec<(String, u64)> =
reference.iter().map(|(k, v)| (k.clone(), *v)).collect();
assert_eq!(ours, theirs, "step {step}: contents disagreed");
assert_eq!(
map.keys().cloned().collect::<Vec<_>>(),
order,
"step {step}: order disagreed"
);
}
}
assert!(!map.is_empty(), "the script never exercised a filled map");
}
#[test]
fn entry_vacant_and_occupied() {
let mut map: OrderedMap<Vec<u32>> = OrderedMap::new();
map.entry("a".into()).or_default().push(1);
map.entry("a".into()).or_default().push(2);
assert_eq!(map.get("a"), Some(&vec![1, 2]));
assert_eq!(map.len(), 1);
*map.entry("b".into()).or_insert(vec![9]) = vec![8];
assert_eq!(map.get("b"), Some(&vec![8]));
let mut calls = 0;
map.entry("b".into()).or_insert_with(|| {
calls += 1;
vec![7]
});
assert_eq!(calls, 0, "or_insert_with ran for an occupied entry");
map.entry("c".into()).or_insert_with(|| {
calls += 1;
vec![7]
});
assert_eq!(calls, 1);
map.entry("c".into()).and_modify(|v| v.push(6)).or_default();
assert_eq!(map.get("c"), Some(&vec![7, 6]));
map.entry("d".into()).and_modify(|v| v.push(5)).or_default();
assert_eq!(map.get("d"), Some(&vec![]));
assert_eq!(
map.keys().map(String::as_str).collect::<Vec<_>>(),
["a", "b", "c", "d"]
);
match map.entry("a".into()) {
Entry::Occupied(mut e) => {
assert_eq!(e.key(), "a");
assert_eq!(e.get(), &vec![1, 2]);
e.get_mut().push(3);
assert_eq!(e.insert(vec![0]), vec![1, 2, 3]);
}
Entry::Vacant(_) => panic!("a is present"),
}
assert_eq!(map.get("a"), Some(&vec![0]));
assert_eq!(map.first().map(|(k, _)| k), Some("a"), "entry moved a key");
match map.entry("zz".into()) {
Entry::Vacant(e) => {
assert_eq!(e.key(), "zz");
assert_eq!(e.into_key(), "zz");
}
Entry::Occupied(_) => panic!("zz is absent"),
}
assert_eq!(map.len(), 4, "into_key inserted something");
}
#[test]
fn entry_removal_keeps_order() {
let mut map = map_of(30);
match map.entry(nth_key(10)) {
Entry::Occupied(e) => assert_eq!(e.remove(), 10),
Entry::Vacant(_) => panic!("present"),
}
assert_index_consistent(&map);
assert_eq!(map.len(), 29);
assert_eq!(map.get(&nth_key(10)), None);
assert_eq!(map.get_index(10), Some((nth_key(11).as_str(), &11)));
}
#[test]
fn entry_stays_indexed_above_the_threshold() {
let mut map = map_of(100);
assert!(map.table.is_some());
for i in 0..100 {
assert_eq!(*map.entry(nth_key(i)).or_insert(usize::MAX), i);
}
assert_eq!(map.len(), 100);
assert_eq!(*map.entry(nth_key(500)).or_insert(7), 7);
assert_eq!(map.len(), 101);
assert_index_consistent(&map);
}
#[test]
fn equality_ignores_order() {
let mut forwards: OrderedMap<i32> = OrderedMap::new();
let mut backwards: OrderedMap<i32> = OrderedMap::new();
for i in 0..40 {
forwards.insert(nth_key(i), i as i32);
}
for i in (0..40).rev() {
backwards.insert(nth_key(i), i as i32);
}
assert_ne!(
forwards.keys().collect::<Vec<_>>(),
backwards.keys().collect::<Vec<_>>(),
"the two maps are supposed to differ in order"
);
assert_eq!(forwards, backwards);
let mut differing = backwards.clone();
differing.insert(nth_key(7), -1);
assert_ne!(forwards, differing);
let mut shorter = forwards.clone();
shorter.remove(&nth_key(39));
assert_ne!(forwards, shorter);
assert_ne!(shorter, forwards);
let empty: OrderedMap<i32> = OrderedMap::new();
assert_eq!(empty, OrderedMap::new());
assert_ne!(empty, forwards);
let mut other: OrderedMap<i32> = OrderedMap::new();
for i in 100..140 {
other.insert(nth_key(i), i as i32);
}
assert_ne!(forwards, other);
}
#[test]
fn clone_keeps_order_and_lookups() {
let map = map_of(70);
let copy = map.clone();
assert_eq!(map, copy);
assert_eq!(
map.keys().collect::<Vec<_>>(),
copy.keys().collect::<Vec<_>>()
);
for i in 0..70 {
assert_eq!(copy.get(&nth_key(i)), Some(&i));
}
assert_index_consistent(©);
}
#[test]
fn sort_keys_reorders_and_lookups_still_work() {
for n in [0usize, 1, 8, 9, 65, 300] {
let mut map: OrderedMap<usize> = OrderedMap::new();
for i in (0..n).rev() {
map.insert(nth_key(i), i);
}
map.sort_keys();
assert_index_consistent(&map);
let mut expected: Vec<String> = (0..n).map(nth_key).collect();
expected.sort();
assert_eq!(map.keys().cloned().collect::<Vec<_>>(), expected);
for i in 0..n {
assert_eq!(map.get(&nth_key(i)), Some(&i), "lost a key at n = {n}");
}
assert_eq!(
map.first().map(|(k, _)| k.to_string()),
expected.first().cloned()
);
assert_eq!(
map.last().map(|(k, _)| k.to_string()),
expected.last().cloned()
);
}
}
#[test]
fn positional_accessors() {
let mut map = map_of(20);
assert_eq!(map.first(), Some((nth_key(0).as_str(), &0)));
assert_eq!(map.last(), Some((nth_key(19).as_str(), &19)));
assert_eq!(map.get_index(20), None);
assert_eq!(map.get_index_mut(20).map(|(_, v)| *v), None);
if let Some((key, value)) = map.get_index_mut(5) {
assert_eq!(key, nth_key(5));
*value = 500;
}
assert_eq!(map.get(&nth_key(5)), Some(&500));
}
#[test]
fn retain_keeps_order_and_rebuilds() {
let mut map = map_of(100);
map.retain(|_, value| *value % 3 == 0);
assert_index_consistent(&map);
let expected: Vec<String> = (0..100).filter(|i| i % 3 == 0).map(nth_key).collect();
assert_eq!(map.keys().cloned().collect::<Vec<_>>(), expected);
for i in 0..100 {
let found = map.get(&nth_key(i));
assert_eq!(found, if i % 3 == 0 { Some(&i) } else { None });
}
map.retain(|key, _| key == nth_key(0));
assert_eq!(map.len(), 1);
assert!(map.table.is_none());
assert_eq!(map.get(&nth_key(0)), Some(&0));
}
#[test]
fn iterators_agree_on_order() {
let mut map = map_of(30);
let keys: Vec<String> = (0..30).map(nth_key).collect();
assert_eq!(map.iter().len(), 30);
assert_eq!(map.keys().cloned().collect::<Vec<_>>(), keys);
assert_eq!(
map.values().copied().collect::<Vec<_>>(),
(0..30).collect::<Vec<_>>()
);
assert_eq!(
map.iter().rev().map(|(k, _)| k.clone()).collect::<Vec<_>>(),
keys.iter().rev().cloned().collect::<Vec<_>>()
);
for (i, (key, value)) in (&mut map).into_iter().enumerate() {
assert_eq!(key, &keys[i]);
*value += 1000;
}
for value in map.values_mut() {
*value += 1;
}
assert_eq!(map.get(&keys[0]), Some(&1001));
let owned: Vec<(String, usize)> = map.clone().into_iter().collect();
assert_eq!(owned.len(), 30);
assert_eq!(owned[0].0, keys[0]);
let rebuilt: OrderedMap<usize> = owned.into_iter().collect();
assert_eq!(rebuilt, map);
assert_eq!(rebuilt.keys().cloned().collect::<Vec<_>>(), keys);
assert_index_consistent(&rebuilt);
}
#[test]
fn from_array_and_extend() {
let mut map = OrderedMap::from([
("b".to_string(), 2),
("a".to_string(), 1),
("b".to_string(), 3),
]);
assert_eq!(map.len(), 2);
assert_eq!(map.get("b"), Some(&3));
assert_eq!(
map.keys().map(String::as_str).collect::<Vec<_>>(),
["b", "a"]
);
map.extend([("c".to_string(), 4), ("a".to_string(), 5)]);
assert_eq!(
map.keys().map(String::as_str).collect::<Vec<_>>(),
["b", "a", "c"]
);
assert_eq!(map["a"], 5);
assert_eq!(map["c"], 4);
}
#[test]
#[should_panic(expected = "no entry found for key")]
fn index_panics_on_a_missing_key() {
let map = map_of(3);
let _ = map["nope"];
}
#[test]
fn debug_prints_as_a_map_in_order() {
let map = OrderedMap::from([("z".to_string(), 1), ("a".to_string(), 2)]);
assert_eq!(format!("{map:?}"), r#"{"z": 1, "a": 2}"#);
}
#[test]
fn capacity_is_honoured() {
let mut map: OrderedMap<u8> = OrderedMap::with_capacity(32);
assert!(map.capacity() >= 32);
map.reserve(100);
assert!(map.capacity() >= 100);
assert!(map.is_empty());
}
#[test]
fn values_of_a_type_that_is_neither_clone_nor_default() {
struct NotCloneable(u8);
let mut map: OrderedMap<NotCloneable> = OrderedMap::default();
map.insert("x".into(), NotCloneable(1));
assert_eq!(map.get("x").map(|v| v.0), Some(1));
}
#[test]
#[cfg(target_pointer_width = "64")]
fn the_map_is_four_words_wide() {
assert_eq!(size_of::<OrderedMap<u64>>(), 32);
}
}