Skip to content

Commit

Permalink
Parameterize the index type of maps and sets
Browse files Browse the repository at this point in the history
The index type is a new generic parameter defaulting to `usize`:

    pub struct IndexSet<T, S = RandomState, Idx = usize>
    pub struct IndexMap<K, V, S = RandomState, Idx = usize>

In `no_std` builds, `S` has no default, but still `Idx = usize`.

This may be used to optimized the storage size in the raw table, for
example using a `u32` index if you know that `u32::MAX` capacity is
sufficient for you. It may also be useful to define a newtype index
custom to a particular domain, which only needs to implement the new
`Indexable` trait to be useful here.
  • Loading branch information
cuviper committed Aug 9, 2020
1 parent 622dd20 commit b61fdd4
Show file tree
Hide file tree
Showing 12 changed files with 595 additions and 285 deletions.
50 changes: 50 additions & 0 deletions examples/custom_index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use fxhash::FxBuildHasher;
use indexmap::{IndexMap, IndexSet, Indexable};
use std::convert::TryFrom;
use std::ops::{Index, IndexMut};

fn main() {
let map: FancyMap<_, _> = (-10..=10).map(|i| (i, i * i)).collect();
let set: FancySet<_> = map.values().copied().collect();
println!("map to squares: {:?}", map);
println!("unique squares: {:?}", set);
}

/// A custom index newtype ensures it can't be confused with indexes for
/// unrelated containers. This one is also smaller to reduce map memory,
/// which also reduces the maximum capacity.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct FancyIndex(u16);

pub type FancyMap<K, V> = IndexMap<K, V, FxBuildHasher, FancyIndex>;
pub type FancySet<T> = IndexSet<T, FxBuildHasher, FancyIndex>;

impl Indexable for FancyIndex {
fn try_from_usize(i: usize) -> Option<Self> {
u16::try_from(i).ok().map(FancyIndex)
}

fn try_into_usize(self) -> Option<usize> {
Some(self.0.into())
}
}

impl<K, V> Index<FancyIndex> for FancyMap<K, V> {
type Output = V;

fn index(&self, index: FancyIndex) -> &V {
let (_key, value) = self
.get_index(index)
.expect("IndexMap: index out of bounds");
value
}
}

impl<K, V> IndexMut<FancyIndex> for FancyMap<K, V> {
fn index_mut(&mut self, index: FancyIndex) -> &mut V {
let (_key, value) = self
.get_index_mut(index)
.expect("IndexMap: index out of bounds");
value
}
}
89 changes: 89 additions & 0 deletions src/indexable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::IndexMap;
use core::ops::{Index, IndexMut};

/// Indexable types can convert to and from `usize`.
pub trait Indexable: Sized + Copy + Ord + Send + Sync {
/// Creates an index from a `usize` or returns `None`.
fn try_from_usize(i: usize) -> Option<Self>;

/// Creates an index from a `usize`; panics on failure.
#[inline]
fn from_usize(i: usize) -> Self {
Self::try_from_usize(i).expect("invalid index!")
}

/// Converts the index to a `usize` or returns `None`.
fn try_into_usize(self) -> Option<usize>;

/// Converts the index to a `usize`; panics on failure.
#[inline]
fn into_usize(self) -> usize {
self.try_into_usize().expect("invalid index!")
}
}

impl Indexable for usize {
#[inline]
fn try_from_usize(i: usize) -> Option<Self> {
Some(i)
}

#[inline]
fn from_usize(i: usize) -> Self {
i
}

#[inline]
fn try_into_usize(self) -> Option<usize> {
Some(self)
}

#[inline]
fn into_usize(self) -> usize {
self
}
}

macro_rules! impl_indexable {
($($ty:ident)*) => {$(
impl Indexable for $ty {
#[inline]
fn try_from_usize(i: usize) -> Option<Self> {
if i <= core::$ty::MAX as usize {
Some(i as $ty)
} else {
None
}
}

#[inline]
fn try_into_usize(self) -> Option<usize> {
if self <= core::usize::MAX as $ty {
Some(self as usize)
} else {
None
}
}
}

impl<K, V, S> Index<$ty> for IndexMap<K, V, S, $ty> {
type Output = V;

fn index(&self, index: $ty) -> &V {
self.get_index(index)
.expect("IndexMap: index out of bounds")
.1
}
}

impl<K, V, S> IndexMut<$ty> for IndexMap<K, V, S, $ty> {
fn index_mut(&mut self, index: $ty) -> &mut V {
self.get_index_mut(index)
.expect("IndexMap: index out of bounds")
.1
}
}
)*}
}

impl_indexable!(u8 u16 u32 u64 u128);
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ use std::vec::{self, Vec};
#[macro_use]
mod macros;
mod equivalent;
mod indexable;
mod mutable_keys;
#[cfg(feature = "serde-1")]
mod serde;
Expand All @@ -108,6 +109,7 @@ pub mod set;
mod rayon;

pub use crate::equivalent::Equivalent;
pub use crate::indexable::Indexable;
pub use crate::map::IndexMap;
pub use crate::set::IndexSet;

Expand Down Expand Up @@ -180,11 +182,16 @@ impl<K, V> Bucket<K, V> {
}
}

/// Get basic unbounded access to entries
trait Entries {
type Entry;
fn into_entries(self) -> Vec<Self::Entry>;
fn as_entries(&self) -> &[Self::Entry];
fn as_entries_mut(&mut self) -> &mut [Self::Entry];
}

/// Borrow entries and fix the indices afterward
trait WithEntries: Entries {
fn with_entries<F>(&mut self, f: F)
where
F: FnOnce(&mut [Self::Entry]);
Expand Down
Loading

0 comments on commit b61fdd4

Please sign in to comment.