Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SecondaryMap for HashSet and HashMap #60

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
SecondaryMap impl for HashSet and HashMap
  • Loading branch information
aborgna-q committed Jun 7, 2023
commit d1a746289645f87ca78c9cba997d7003ca8cbfe4
4 changes: 3 additions & 1 deletion src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use std::mem::{replace, take};
use thiserror::Error;

use crate::unmanaged::UnmanagedDenseMap;
use crate::NodeIndex;
use crate::{impl_static_default, NodeIndex};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -487,6 +487,8 @@ impl Default for NodeData {
}
}

impl_static_default!(NodeData, NodeData::new());

/// Iterator created by [`Hierarchy::children`].
#[derive(Clone)]
pub struct Children<'a> {
Expand Down
66 changes: 49 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,17 @@ impl NodeIndex {
pub fn index(self) -> usize {
self.into()
}

/// Constant implementation of TryFrom<usize>
#[inline]
const fn try_from_usize(index: usize) -> Result<Self, IndexError> {
if index > Self::MAX {
Err(IndexError { index })
} else {
// SAFETY: The value cannot be zero
Ok(Self(unsafe { NonZeroU32::new_unchecked(1 + index as u32) }))
}
}
}

impl From<NodeIndex> for usize {
Expand All @@ -176,12 +187,7 @@ impl TryFrom<usize> for NodeIndex {

#[inline]
fn try_from(index: usize) -> Result<Self, Self::Error> {
if index > Self::MAX {
Err(IndexError { index })
} else {
// SAFETY: The value cannot be zero
Ok(Self(unsafe { NonZeroU32::new_unchecked(1 + index as u32) }))
}
Self::try_from_usize(index)
}
}

Expand All @@ -192,6 +198,15 @@ impl std::fmt::Debug for NodeIndex {
}
}

impl_static_default!(
NodeIndex,
match NodeIndex::try_from_usize(0) {
Ok(index) => index,
// Zero is always a valid index
Err(_) => unreachable!(),
}
);

/// Index of a port within a `PortGraph`.
///
/// Restricted to be at most `2^31 - 1` to allow more efficient encodings of the port graph structure.
Expand Down Expand Up @@ -221,6 +236,17 @@ impl PortIndex {
pub fn index(self) -> usize {
self.into()
}

/// Constant implementation of TryFrom<usize>
#[inline]
const fn try_from_usize(index: usize) -> Result<Self, IndexError> {
if index > Self::MAX {
Err(IndexError { index })
} else {
// SAFETY: The value cannot be zero
Ok(Self(unsafe { NonZeroU32::new_unchecked(1 + index as u32) }))
}
}
}

impl From<PortIndex> for usize {
Expand All @@ -235,12 +261,7 @@ impl TryFrom<usize> for PortIndex {

#[inline]
fn try_from(index: usize) -> Result<Self, Self::Error> {
if index > Self::MAX {
Err(IndexError { index })
} else {
// SAFETY: The value cannot be zero
Ok(Self(unsafe { NonZeroU32::new_unchecked(1 + index as u32) }))
}
Self::try_from_usize(index)
}
}

Expand All @@ -257,6 +278,15 @@ impl Default for PortIndex {
}
}

impl_static_default!(
PortIndex,
match PortIndex::try_from_usize(0) {
Ok(index) => index,
// Zero is always a valid index
Err(_) => unreachable!(),
}
);

/// Error indicating a `NodeIndex`, `PortIndex`, or `Direction` is too large.
#[derive(Debug, Clone, Error, PartialEq, Eq)]
#[error("the index {index} is too large.")]
Expand All @@ -281,7 +311,7 @@ pub enum PortOffset {
impl PortOffset {
/// Creates a new port offset.
#[inline(always)]
pub fn new(direction: Direction, offset: usize) -> Self {
pub const fn new(direction: Direction, offset: usize) -> Self {
match direction {
Direction::Incoming => Self::new_incoming(offset),
Direction::Outgoing => Self::new_outgoing(offset),
Expand All @@ -290,7 +320,7 @@ impl PortOffset {

/// Creates a new incoming port offset.
#[inline(always)]
pub fn new_incoming(offset: usize) -> Self {
pub const fn new_incoming(offset: usize) -> Self {
assert!(offset < u16::MAX as usize);
// SAFETY: The value cannot be zero
let offset = unsafe { NonZeroU16::new_unchecked(offset.saturating_add(1) as u16) };
Expand All @@ -299,14 +329,14 @@ impl PortOffset {

/// Creates a new outgoing port offset.
#[inline(always)]
pub fn new_outgoing(offset: usize) -> Self {
pub const fn new_outgoing(offset: usize) -> Self {
assert!(offset <= u16::MAX as usize);
PortOffset::Outgoing(offset as u16)
}

/// Returns the direction of the port.
#[inline(always)]
pub fn direction(self) -> Direction {
pub const fn direction(self) -> Direction {
match self {
PortOffset::Incoming(_) => Direction::Incoming,
PortOffset::Outgoing(_) => Direction::Outgoing,
Expand All @@ -315,7 +345,7 @@ impl PortOffset {

/// Returns the offset of the port.
#[inline(always)]
pub fn index(self) -> usize {
pub const fn index(self) -> usize {
match self {
PortOffset::Incoming(offset) => (offset.get() - 1) as usize,
PortOffset::Outgoing(offset) => offset as usize,
Expand All @@ -337,3 +367,5 @@ impl std::fmt::Debug for PortOffset {
}
}
}

impl_static_default!(PortOffset, PortOffset::new_outgoing(0));
124 changes: 123 additions & 1 deletion src/secondary.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Trait definition for secondary maps from keys to values with default elements.

use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::{hash::Hash, iter::FusedIterator};

use bitvec::{
Expand Down Expand Up @@ -333,3 +333,125 @@ where
self.iter.size_hint()
}
}

impl<K, V> SecondaryMap<K, V> for HashMap<K, V>
where
K: Hash + Eq + Clone,
V: StaticDefault + Eq + Clone,
{
type Iter<'a> = HashMapIter<'a, K, V> where Self: 'a, K: 'a;

#[inline]
fn new() -> Self {
HashMap::new()
}

#[inline]
fn with_capacity(capacity: usize) -> Self {
HashMap::with_capacity(capacity)
}

#[inline]
fn default_value(&self) -> V {
V::default_ref().clone()
}

#[inline]
fn ensure_capacity(&mut self, capacity: usize) {
HashMap::reserve(self, capacity.saturating_sub(self.capacity()));
}

#[inline]
fn resize(&mut self, _new_len: usize) {}

#[inline]
fn capacity(&self) -> usize {
HashMap::capacity(self)
}

#[inline]
fn get(&self, key: K) -> &V {
HashMap::get(self, &key).unwrap_or(V::default_ref())
}

#[inline]
fn set(&mut self, key: K, val: V) {
match &val == V::default_ref() {
true => HashMap::insert(self, key, val),
false => HashMap::remove(self, &key),
};
}

#[inline]
fn take(&mut self, key: K) -> V {
HashMap::remove(self, &key).unwrap_or(self.default_value())
}

#[inline]
fn iter<'a>(&'a self) -> Self::Iter<'a>
where
K: 'a,
{
HashMapIter {
iter: HashMap::iter(self),
}
}
}

/// Iterator over non-default entries of a bit vector secondary map.
#[derive(Debug, Clone)]
pub struct HashMapIter<'a, K, V> {
iter: std::collections::hash_map::Iter<'a, K, V>,
}

impl<'a, K, V> Iterator for HashMapIter<'a, K, V>
where
K: Clone,
{
type Item = (K, &'a V);

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(k, v)| (k.clone(), v))
}

#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.iter.nth(n).map(|(k, v)| (k.clone(), v))
}

#[inline]
fn count(self) -> usize {
self.iter.count()
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

/// A trait for secondary map values that can provide a static reference to a default value.
pub trait StaticDefault: 'static {
/// Returns a static reference to the default value
fn default_ref<'a>() -> &'a Self;
}

/// Implements the `StaticDefault` trait for a type, using a const element.
#[allow(unused_macros)]
#[macro_export]
macro_rules! impl_static_default {
($name:ident, $default:expr) => {
impl $crate::secondary::StaticDefault for $name {
fn default_ref<'a>() -> &'a Self {
static DEFAULT: $name = $default;
&DEFAULT
}
}
};
}
#[allow(unused_imports)]
pub use impl_static_default;

impl_static_default!(bool, false);
impl_static_default!(usize, 0);