diff --git a/Cargo.toml b/Cargo.toml index d036680..8179839 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,9 @@ name = "mule-map" version = "0.1.0" edition = "2021" +[lints.clippy] +pedantic = { level = "warn", priority = -1 } + [dependencies] num-traits = { version = "0.2.19", features = ["i128"] } paste = "1.0.15" diff --git a/compare/Cargo.lock b/compare/Cargo.lock index 5cc36d2..26927b6 100644 --- a/compare/Cargo.lock +++ b/compare/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -290,8 +290,8 @@ name = "mule-map" version = "0.1.0" dependencies = [ "num-traits", + "paste", "sealed", - "static_assertions", ] [[package]] @@ -528,12 +528,6 @@ dependencies = [ "serde", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "syn" version = "2.0.87" diff --git a/src/entry.rs b/src/entry.rs index 69fcc4d..b38d9ef 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,281 +1,284 @@ -use crate::Entry::{Occupied, Vacant}; -use num_traits::PrimInt; +pub(crate) mod private { + use num_traits::PrimInt; + use Entry::{Occupied, Vacant}; -pub enum Entry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { - Occupied(OccupiedEntry<'a, K, V, ZERO_IS_SENTINEL>), - Vacant(VacantEntry<'a, K, V, ZERO_IS_SENTINEL>), -} + pub enum Entry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { + Occupied(OccupiedEntry<'a, K, V, ZERO_IS_SENTINEL>), + Vacant(VacantEntry<'a, K, V, ZERO_IS_SENTINEL>), + } -pub enum OccupiedEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { - HashMap(OccupiedHashMapEntry<'a, K, V>), - Vec(OccupiedVecEntry<'a, K, V, ZERO_IS_SENTINEL>), -} + pub enum OccupiedEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { + HashMap(OccupiedHashMapEntry<'a, K, V>), + Vec(OccupiedVecEntry<'a, K, V, ZERO_IS_SENTINEL>), + } -pub enum VacantEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { - HashMap(VacantHashMapEntry<'a, K, V>), - Vec(VacantVecEntry<'a, K, V, ZERO_IS_SENTINEL>), -} + pub enum VacantEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { + HashMap(VacantHashMapEntry<'a, K, V>), + Vec(VacantVecEntry<'a, K, V, ZERO_IS_SENTINEL>), + } -pub struct OccupiedHashMapEntry<'a, K: 'a, V: 'a> { - pub(crate) base: std::collections::hash_map::OccupiedEntry<'a, K, V>, -} + pub struct OccupiedHashMapEntry<'a, K: 'a, V: 'a> { + pub(crate) base: std::collections::hash_map::OccupiedEntry<'a, K, V>, + } -pub struct OccupiedVecEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { - pub(crate) value: &'a mut V, - pub(crate) validity: &'a mut bool, - pub(crate) key: K, -} + pub struct OccupiedVecEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { + pub(crate) value: &'a mut V, + pub(crate) occupied: &'a mut bool, + pub(crate) key: K, + } -pub struct VacantHashMapEntry<'a, K: 'a, V: 'a> { - pub(crate) base: std::collections::hash_map::VacantEntry<'a, K, V>, -} + pub struct VacantHashMapEntry<'a, K: 'a, V: 'a> { + pub(crate) base: std::collections::hash_map::VacantEntry<'a, K, V>, + } -pub struct VacantVecEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { - pub(crate) value: &'a mut V, - pub(crate) validity: &'a mut bool, - pub(crate) key: K, -} + pub struct VacantVecEntry<'a, K: 'a, V: 'a, const ZERO_IS_SENTINEL: bool> { + pub(crate) value: &'a mut V, + pub(crate) occupied: &'a mut bool, + pub(crate) key: K, + } -impl<'a, K, V, const ZERO_IS_SENTINEL: bool> Entry<'a, K, V, ZERO_IS_SENTINEL> -where - K: PrimInt, - V: std::default::Default, -{ - #[inline] - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default), + impl<'a, K, V, const ZERO_IS_SENTINEL: bool> Entry<'a, K, V, ZERO_IS_SENTINEL> + where + K: PrimInt, + V: std::default::Default, + { + #[inline] + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default), + } } - } - #[inline] - pub fn or_insert_with V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default()), + #[inline] + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default()), + } } - } - #[inline] - pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => { - let value = default(entry.key()); - entry.insert(value) + #[inline] + pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let value = default(entry.key()); + entry.insert(value) + } } } - } - #[inline] - pub fn key(&self) -> K { - match *self { - Occupied(ref entry) => entry.key(), - Vacant(ref entry) => entry.key(), + #[inline] + pub fn key(&self) -> K { + match *self { + Occupied(ref entry) => entry.key(), + Vacant(ref entry) => entry.key(), + } } - } - #[inline] - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut V), - { - match self { - Occupied(mut entry) => { - f(entry.get_mut()); - Occupied(entry) + #[inline] + #[allow(clippy::return_self_not_must_use)] + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut V), + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + } + Vacant(entry) => Vacant(entry), } - Vacant(entry) => Vacant(entry), } - } - //Not till rust 1.83.0 - // pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - // match self { - // Occupied(mut entry) => { - // entry.insert(value); - // entry - // } - // Vacant(entry) => entry.insert_entry(value), - // } - // } -} - -impl<'a, K, V, const ZERO_IS_SENTINEL: bool> OccupiedEntry<'a, K, V, ZERO_IS_SENTINEL> -where - K: PrimInt, - V: std::default::Default, -{ - #[inline] - pub fn key(&self) -> K { - match self { - OccupiedEntry::HashMap(ref entry) => *entry.base.key(), - OccupiedEntry::Vec(ref entry) => entry.key(), - } + //Not till rust 1.83.0 + // pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + // match self { + // Occupied(mut entry) => { + // entry.insert(value); + // entry + // } + // Vacant(entry) => entry.insert_entry(value), + // } + // } } - #[inline] - pub fn remove_entry(self) -> (K, V) + + impl<'a, K, V, const ZERO_IS_SENTINEL: bool> OccupiedEntry<'a, K, V, ZERO_IS_SENTINEL> where - V: Clone, + K: PrimInt, + V: std::default::Default, { - match self { - OccupiedEntry::HashMap(entry) => entry.base.remove_entry(), - OccupiedEntry::Vec(entry) => entry.remove_entry(), + #[inline] + pub fn key(&self) -> K { + match self { + OccupiedEntry::HashMap(ref entry) => *entry.base.key(), + OccupiedEntry::Vec(ref entry) => entry.key(), + } } - } - #[inline] - pub fn get(&self) -> &V { - match self { - OccupiedEntry::HashMap(ref entry) => entry.base.get(), - OccupiedEntry::Vec(ref entry) => entry.get(), + #[inline] + pub fn remove_entry(self) -> (K, V) + where + V: Clone, + { + match self { + OccupiedEntry::HashMap(entry) => entry.base.remove_entry(), + OccupiedEntry::Vec(entry) => entry.remove_entry(), + } } - } - #[inline] - pub fn get_mut(&mut self) -> &mut V { - match self { - OccupiedEntry::HashMap(ref mut entry) => entry.base.get_mut(), - OccupiedEntry::Vec(ref mut entry) => entry.get_mut(), + #[inline] + pub fn get(&self) -> &V { + match self { + OccupiedEntry::HashMap(ref entry) => entry.base.get(), + OccupiedEntry::Vec(ref entry) => entry.get(), + } } - } - #[inline] - pub fn into_mut(self) -> &'a mut V { - match self { - OccupiedEntry::HashMap(entry) => entry.base.into_mut(), - OccupiedEntry::Vec(entry) => entry.into_mut(), + #[inline] + pub fn get_mut(&mut self) -> &mut V { + match self { + OccupiedEntry::HashMap(ref mut entry) => entry.base.get_mut(), + OccupiedEntry::Vec(ref mut entry) => entry.get_mut(), + } } - } - #[inline] - pub fn insert(&mut self, value: V) -> V { - match self { - OccupiedEntry::HashMap(ref mut entry) => entry.base.insert(value), - OccupiedEntry::Vec(ref mut entry) => entry.insert(value), + #[inline] + pub fn into_mut(self) -> &'a mut V { + match self { + OccupiedEntry::HashMap(entry) => entry.base.into_mut(), + OccupiedEntry::Vec(entry) => entry.into_mut(), + } } - } - #[inline] - pub fn remove(self) -> V - where - V: Clone, - { - match self { - OccupiedEntry::HashMap(entry) => entry.base.remove(), - OccupiedEntry::Vec(entry) => entry.remove(), + #[inline] + pub fn insert(&mut self, value: V) -> V { + match self { + OccupiedEntry::HashMap(ref mut entry) => entry.base.insert(value), + OccupiedEntry::Vec(ref mut entry) => entry.insert(value), + } + } + #[inline] + pub fn remove(self) -> V + where + V: Clone, + { + match self { + OccupiedEntry::HashMap(entry) => entry.base.remove(), + OccupiedEntry::Vec(entry) => entry.remove(), + } } } -} -impl<'a, K, V, const ZERO_IS_SENTINEL: bool> OccupiedVecEntry<'a, K, V, ZERO_IS_SENTINEL> -where - K: PrimInt, - V: std::default::Default, -{ - #[inline] - pub fn key(&self) -> K { - self.key - } - #[inline] - pub fn remove_entry(self) -> (K, V) + impl<'a, K, V, const ZERO_IS_SENTINEL: bool> OccupiedVecEntry<'a, K, V, ZERO_IS_SENTINEL> where - V: Clone, + K: PrimInt, + V: std::default::Default, { - if ZERO_IS_SENTINEL { - *self.value = Default::default(); - (self.key, self.value.clone()) - } else { - *self.validity = false; - (self.key, self.value.clone()) + #[inline] + pub fn key(&self) -> K { + self.key } - } - #[inline] - pub fn get(&self) -> &V { - self.value - } - #[inline] - pub fn get_mut(&mut self) -> &mut V { - self.value - } - #[inline] - pub fn into_mut(self) -> &'a mut V { - self.value - } - #[inline] - pub fn insert(&mut self, value: V) -> V { - let mut value = value; - std::mem::swap(&mut value, self.get_mut()); - value - } - #[inline] - pub fn remove(self) -> V - where - V: Clone, - { - if ZERO_IS_SENTINEL { - let value = self.value.clone(); - *self.value = Default::default(); + #[inline] + pub fn remove_entry(self) -> (K, V) + where + V: Clone, + { + if ZERO_IS_SENTINEL { + *self.value = Default::default(); + (self.key, self.value.clone()) + } else { + *self.occupied = false; + (self.key, self.value.clone()) + } + } + #[inline] + pub fn get(&self) -> &V { + self.value + } + #[inline] + pub fn get_mut(&mut self) -> &mut V { + self.value + } + #[inline] + pub fn into_mut(self) -> &'a mut V { + self.value + } + #[inline] + pub fn insert(&mut self, value: V) -> V { + let mut value = value; + std::mem::swap(&mut value, self.get_mut()); value - } else { - *self.validity = false; - self.value.clone() + } + #[inline] + pub fn remove(self) -> V + where + V: Clone, + { + if ZERO_IS_SENTINEL { + let value = self.value.clone(); + *self.value = Default::default(); + value + } else { + *self.occupied = false; + self.value.clone() + } } } -} -impl<'a, K, V, const ZERO_IS_SENTINEL: bool> VacantEntry<'a, K, V, ZERO_IS_SENTINEL> -where - K: PrimInt, -{ - #[inline] - pub fn key(&self) -> K { - match self { - VacantEntry::HashMap(ref entry) => *entry.base.key(), - VacantEntry::Vec(ref entry) => entry.key(), + impl<'a, K, V, const ZERO_IS_SENTINEL: bool> VacantEntry<'a, K, V, ZERO_IS_SENTINEL> + where + K: PrimInt, + { + #[inline] + pub fn key(&self) -> K { + match self { + VacantEntry::HashMap(ref entry) => *entry.base.key(), + VacantEntry::Vec(ref entry) => entry.key(), + } } - } - #[inline] - pub fn insert(self, value: V) -> &'a mut V { - match self { - VacantEntry::HashMap(entry) => entry.base.insert(value), - VacantEntry::Vec(entry) => entry.insert(value), + #[inline] + pub fn insert(self, value: V) -> &'a mut V { + match self { + VacantEntry::HashMap(entry) => entry.base.insert(value), + VacantEntry::Vec(entry) => entry.insert(value), + } } - } - - //Not till rust 1.83.0 - // pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - // match self { - // VacantEntry::HashMap(entry) => entry::OccupiedEntry { - // base: entry.base.insert_entry(value), - // }, - // VacantEntry::Vec(entry) => entry.insert_entry(value), - // } - // } -} -impl<'a, K, V, const ZERO_IS_SENTINEL: bool> VacantVecEntry<'a, K, V, ZERO_IS_SENTINEL> -where - K: PrimInt, -{ - #[inline] - pub fn key(&self) -> K { - self.key + //Not till rust 1.83.0 + // pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + // match self { + // VacantEntry::HashMap(entry) => entry::OccupiedEntry { + // base: entry.base.insert_entry(value), + // }, + // VacantEntry::Vec(entry) => entry.insert_entry(value), + // } + // } } - #[inline] - pub fn insert(self, value: V) -> &'a mut V { - if ZERO_IS_SENTINEL { - *self.value = value; - self.value - } else { - *self.validity = true; - *self.value = value; - self.value + + impl<'a, K, V, const ZERO_IS_SENTINEL: bool> VacantVecEntry<'a, K, V, ZERO_IS_SENTINEL> + where + K: PrimInt, + { + #[inline] + pub fn key(&self) -> K { + self.key + } + #[inline] + pub fn insert(self, value: V) -> &'a mut V { + if ZERO_IS_SENTINEL { + *self.value = value; + self.value + } else { + *self.occupied = true; + *self.value = value; + self.value + } } - } - //Not till rust 1.83.0 - // pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - // *self.value = value; - // OccupiedVecEntry { - // value: &mut self.value, - // key: self.key, - // sentinel_value: &self.sentinel_value, - // } - // } + //Not till rust 1.83.0 + // pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + // *self.value = value; + // OccupiedVecEntry { + // value: &mut self.value, + // key: self.key, + // sentinel_value: &self.sentinel_value, + // } + // } + } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index c3e0c9d..d8b73e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,13 @@ -use crate::entry::{ +pub use crate::entry::private::{ Entry, OccupiedEntry, OccupiedHashMapEntry, OccupiedVecEntry, VacantEntry, VacantHashMapEntry, VacantVecEntry, }; use num_traits::AsPrimitive; use num_traits::PrimInt; -use paste::paste; use sealed::sealed; use std::collections::HashMap; +use std::fmt::Debug; pub const ZERO_SENTINEL: bool = true; pub const NOT_ZERO_SENTINEL: bool = false; @@ -24,10 +24,30 @@ pub struct MuleMap< const TABLE_MAX_VALUE: i128 = { u8::MAX as i128 }, > { table: Vec, - validity_map: Vec, + occupied_map: Vec, hash_map: HashMap, } +impl< + K, + V, + S, + const ZERO_IS_SENTINEL: bool, + const TABLE_MIN_VALUE: i128, + const TABLE_MAX_VALUE: i128, + > Default for MuleMap +where + K: PrimInt + Eq + std::hash::Hash + KeyIndex + TryFrom + 'static, + S: Default + std::hash::BuildHasher, + V: Clone + PartialEq + Default, + i128: AsPrimitive, + >::Error: Debug, +{ + fn default() -> Self { + Self::new() + } +} + impl< K, V, @@ -37,72 +57,93 @@ impl< const TABLE_MAX_VALUE: i128, > MuleMap where - K: PrimInt + Eq + std::hash::Hash + KeyIndex + AsPrimitive, /*+ std::convert::TryFrom*/ - S: std::default::Default + std::hash::BuildHasher, - V: std::clone::Clone + std::cmp::PartialEq + Default + num_traits::ConstZero, + K: PrimInt + Eq + std::hash::Hash + KeyIndex + TryFrom + 'static, + S: Default + std::hash::BuildHasher, + V: Clone + PartialEq + Default, + i128: AsPrimitive, { const STATIC_ASSERT_MAX_GREATER_OR_EQ_MIN: () = assert!(TABLE_MAX_VALUE >= TABLE_MIN_VALUE); - pub fn new() -> Self { - let _ = Self::STATIC_ASSERT_MAX_GREATER_OR_EQ_MIN; + #[inline] + #[must_use] + fn use_lookup_table(key: K) -> bool { + // `TABLE_MAX_VALUE` and `TABLE_MIN_VALUE` must fit into a key type, K + // Hopfully in the future they can have type K + key <= TABLE_MAX_VALUE.as_() && key >= TABLE_MIN_VALUE.as_() + } + + /// # Panics + /// ... + #[must_use] + pub fn new() -> Self + where + >::Error: Debug, + { + #[allow(clippy::let_unit_value)] + let () = Self::STATIC_ASSERT_MAX_GREATER_OR_EQ_MIN; // NOTE: Can't make this a static assert yet because of try_from assert!(usize::try_from(TABLE_MAX_VALUE - TABLE_MIN_VALUE + 1).is_ok()); - assert!(TABLE_MAX_VALUE <= >::as_(K::max_value())); - assert!(TABLE_MIN_VALUE >= >::as_(K::min_value())); + // Hard limit, way beyond practical lookup table size + assert!(TABLE_MAX_VALUE - TABLE_MIN_VALUE < i128::from(i32::MAX)); + >::try_into(TABLE_MAX_VALUE) + .expect("TABLE_MAX_VALUE should fit into key type, K"); + >::try_into(TABLE_MIN_VALUE) + .expect("TABLE_MAX_VALUE should fit into key type, K"); + + #[allow(clippy::cast_sign_loss)] // Lookup table size can't exceed `usize` let table_size: usize = (TABLE_MAX_VALUE - TABLE_MIN_VALUE + 1) as usize; + let occupied_map_size: usize = if ZERO_IS_SENTINEL == ZERO_SENTINEL { + 1 + } else { + table_size + }; MuleMap:: { table: vec![V::default(); table_size], - validity_map: vec![false; table_size], - hash_map: Default::default(), + occupied_map: vec![false; occupied_map_size], + hash_map: HashMap::default(), } } #[inline] pub fn entry(&mut self, key: K) -> Entry<'_, K, V, ZERO_IS_SENTINEL> { - // if key <= K::from(TABLE_MAX_VALUE).expect("REASON") - // && key >= K::from(TABLE_MIN_VALUE).expect("REASON") - - if >::as_(key) <= TABLE_MAX_VALUE - && >::as_(key) >= TABLE_MIN_VALUE - { + if Self::use_lookup_table(key) { let key_index = key.key_index(); - match ZERO_IS_SENTINEL { - ZERO_SENTINEL => match self.table[key_index] == V::default() { - true => { - Entry::::Vacant(VacantEntry::Vec(VacantVecEntry { - value: &mut self.table[key_index], - validity: &mut self.validity_map[key_index], - key: key, - })) - } - false => Entry::::Occupied(OccupiedEntry::Vec( + if ZERO_IS_SENTINEL == ZERO_SENTINEL { + if self.table[key_index] == V::default() { + Entry::::Vacant(VacantEntry::Vec(VacantVecEntry { + value: &mut self.table[key_index], + occupied: &mut self.occupied_map[0], + key, + })) + } else { + Entry::::Occupied(OccupiedEntry::Vec( OccupiedVecEntry { value: &mut self.table[key_index], - validity: &mut self.validity_map[key_index], - key: key, + occupied: &mut self.occupied_map[0], + key, }, - )), - }, - NOT_ZERO_SENTINEL => match self.validity_map[key_index] { - true => Entry::::Occupied(OccupiedEntry::Vec( + )) + } + } else { + #[allow(clippy::collapsible_else_if)] + if self.occupied_map[key_index] { + Entry::::Occupied(OccupiedEntry::Vec( OccupiedVecEntry { value: &mut self.table[key_index], - validity: &mut self.validity_map[key_index], - key: key, + occupied: &mut self.occupied_map[key_index], + key, }, - )), - - false => { - Entry::::Vacant(VacantEntry::Vec(VacantVecEntry { - value: &mut self.table[key_index], - validity: &mut self.validity_map[key_index], - key: key, - })) - } - }, + )) + } else { + Entry::::Vacant(VacantEntry::Vec(VacantVecEntry { + value: &mut self.table[key_index], + occupied: &mut self.occupied_map[key_index], + key, + })) + } } } else { match self.hash_map.entry(key) { @@ -122,29 +163,25 @@ where #[inline] pub fn get(&self, key: K) -> Option<&V> { - if key <= K::from(TABLE_MAX_VALUE).expect("REASON") - && key >= K::from(TABLE_MIN_VALUE).expect("REASON") - { + if Self::use_lookup_table(key) { let key_index = key.key_index(); - match ZERO_IS_SENTINEL { - ZERO_SENTINEL => { - if self.table[key_index] == V::default() { - return None; - } else { - return Some(&self.table[key_index]); - } + + #[allow(clippy::collapsible_else_if)] + if ZERO_IS_SENTINEL == ZERO_SENTINEL { + if self.table[key_index] == V::default() { + None + } else { + Some(&self.table[key_index]) + } + } else { + if self.occupied_map[key_index] { + Some(&self.table[key_index]) + } else { + None } - NOT_ZERO_SENTINEL => match self.validity_map[key_index] { - true => { - return Some(&self.table[key_index]); - } - false => { - return None; - } - }, } } else { - return self.hash_map.get(&key); + self.hash_map.get(&key) } } @@ -152,20 +189,14 @@ where pub fn bump(&mut self, key: K) where V: std::ops::AddAssign + num_traits::One, - K: AsPrimitive + AsPrimitive + AsPrimitive, { - if >::as_(key) <= TABLE_MAX_VALUE - && >::as_(key) >= TABLE_MIN_VALUE - { + if Self::use_lookup_table(key) { let key_index = key.key_index(); - match ZERO_IS_SENTINEL { - ZERO_SENTINEL => { - self.table[key_index] += V::one(); - } - NOT_ZERO_SENTINEL => { - self.validity_map[key_index] = true; - self.table[key_index] += V::one(); - } + if ZERO_IS_SENTINEL == ZERO_SENTINEL { + self.table[key_index] += V::one(); + } else { + self.occupied_map[key_index] = true; + self.table[key_index] += V::one(); } } else { self.hash_map @@ -179,28 +210,22 @@ where pub fn modify_or_insert(&mut self, key: K, f: F, default: V) where F: FnOnce(&mut V), - K: AsPrimitive + AsPrimitive + AsPrimitive, { - if >::as_(key) <= TABLE_MAX_VALUE - && >::as_(key) >= TABLE_MIN_VALUE - { + if Self::use_lookup_table(key) { let key_index = key.key_index(); - match ZERO_IS_SENTINEL { - ZERO_SENTINEL => { - if self.table[key_index] == Default::default() { - self.table[key_index] = default; - } else { - f(&mut self.table[key_index]) - } + if ZERO_IS_SENTINEL == ZERO_SENTINEL { + if self.table[key_index] == Default::default() { + self.table[key_index] = default; + } else { + f(&mut self.table[key_index]); } - NOT_ZERO_SENTINEL => { - if self.validity_map[key_index] { - f(&mut self.table[key_index]) - } else { - self.table[key_index] = default; - } - self.validity_map[key_index] = true; + } else { + if self.occupied_map[key_index] { + f(&mut self.table[key_index]); + } else { + self.table[key_index] = default; } + self.occupied_map[key_index] = true; } } else { self.hash_map.entry(key).and_modify(f).or_insert(default); @@ -215,79 +240,122 @@ pub trait KeyIndex { #[sealed] impl KeyIndex for u8 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions of unsigned types becasue key >= TABLE_MIN_VALUE (*self - TABLE_MIN_VALUE as u8) as usize } } #[sealed] impl KeyIndex for u16 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions of unsigned types becasue key >= TABLE_MIN_VALUE (*self - TABLE_MIN_VALUE as u16) as usize } } #[sealed] impl KeyIndex for u32 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions of unsigned types becasue key >= TABLE_MIN_VALUE (*self - TABLE_MIN_VALUE as u32) as usize } } #[sealed] impl KeyIndex for u64 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions of unsigned types becasue key >= TABLE_MIN_VALUE (*self - TABLE_MIN_VALUE as u64) as usize } } -// #[sealed] -// impl KeyIndex for u128 { -// fn key_index(&self) -> usize { -// // NOTE: ... -// (*self - TABLE_MIN_VALUE as u128) as usize -// } -// } +#[sealed] +impl KeyIndex for u128 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] + fn key_index(&self) -> usize { + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions of unsigned types becasue key >= TABLE_MIN_VALUE + // NOTE: i128 can't represent u128::MAX, but it's value will still fit in u128 + (*self - TABLE_MIN_VALUE as u128) as usize + } +} #[sealed] impl KeyIndex for i8 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { - (*self as i16 - TABLE_MIN_VALUE as i16) as usize + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: Promotion to i16 _needed_ for subtractions because difference could exceed i8::MAX + (i16::from(*self) - TABLE_MIN_VALUE as i16) as usize } } #[sealed] impl KeyIndex for i16 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { - (*self as i32 - TABLE_MIN_VALUE as i32) as usize + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: Promotion to i32 _needed_ for subtractions because difference could exceed i16::MAX + (i32::from(*self) - TABLE_MIN_VALUE as i32) as usize } } #[sealed] impl KeyIndex for i32 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { - // table size will not exceed limits of i32, no need to promote + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions because difference will be at most i32::MAX - fits in i32 (*self - TABLE_MIN_VALUE as i32) as usize } } #[sealed] impl KeyIndex for i64 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn key_index(&self) -> usize { - // table size will not exceed limits of i64, no need to promote + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions because difference will be at most i32::MAX - fits in i64 (*self - TABLE_MIN_VALUE as i64) as usize } } -// #[sealed] -// impl KeyIndex for i128 { -// fn key_index(&self) -> usize { -// // table size will not exceed limits of i128, no need to promote -// (*self - TABLE_MIN_VALUE) as usize -// } -// } +#[sealed] +impl KeyIndex for i128 { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] + fn key_index(&self) -> usize { + // NOTE: Table size (difference) will not exceed i32::MAX so cast to usize will not truncate + // NOTE: No promotion needed for subtractions because difference will be at most i32::MAX - fits in i128 + (*self - TABLE_MIN_VALUE) as usize + } +} -// MuleMap +/// `ABLE_MIN_VALUE` > `TABLE_MAX_VALUE` +/// ```compile_fail +/// use mule_map::*; +/// let mut mule_map_bad = MuleMap::::new(); +/// +/// ``` +fn _doc_test() {} #[cfg(test)] mod tests { @@ -303,8 +371,5 @@ mod tests { let entry = mule_map.entry(5); entry.or_insert(10); assert_eq!(mule_map.get(5), Some(&10)); - - // let _sbla = newb!(u32); - // let _mule_map = MuleMapU32::::new(); } }