Skip to content

Commit

Permalink
Use const generics instead of typenum.
Browse files Browse the repository at this point in the history
This has the (desired) side effect of making containers covariant over
their input type, so we also add some assertions to ensure that this
doesn't regress.

Fixes bodil#196.
  • Loading branch information
jneem committed May 1, 2022
1 parent 71331ea commit a2d78ec
Show file tree
Hide file tree
Showing 14 changed files with 63 additions and 49 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project
adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- All container types are now covariant over the stored type. (#196)

## [15.1.0] - 2022-04-29

### Added
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ debug = []

[dependencies]
typenum = "1.12"
bitmaps = "2"
sized-chunks = "0.6.4"
bitmaps = "3"
sized-chunks = "0.7"
rand_core = "0.6"
rand_xoshiro = "0.6"
quickcheck = { version = "1", optional = true }
Expand Down
8 changes: 3 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use typenum::*;

/// The branching factor of RRB-trees
pub(crate) type VectorChunkSize = U64;
pub(crate) const VECTOR_CHUNK_SIZE: usize = 64;

/// The branching factor of B-trees
pub(crate) type OrdChunkSize = U64; // Must be an even number!
pub(crate) const ORD_CHUNK_SIZE: usize = 64; // Must be an even number!

/// The level size of HAMTs, in bits
/// Branching factor is 2 ^ HashLevelSize.
pub(crate) type HashLevelSize = U5;
pub(crate) const HASH_LEVEL_SIZE: usize = 5;

/// The size of per-instance memory pools if the `pool` feature is enabled.
/// This is set to 0, meaning you have to opt in to using a pool by constructing
Expand Down
3 changes: 3 additions & 0 deletions src/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2125,6 +2125,9 @@ mod test {
use ::proptest::{collection, proptest};
use std::hash::BuildHasherDefault;

assert_covariant!(HashMap<T, i32> in T);
assert_covariant!(HashMap<i32, T> in T);

#[test]
fn safe_mutation() {
let v1: HashMap<usize, usize> = (0..131_072).map(|i| (i, i)).collect::<HashMap<_, _>>();
Expand Down
2 changes: 2 additions & 0 deletions src/hash/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,8 @@ mod test {
use ::proptest::proptest;
use std::hash::BuildHasherDefault;

assert_covariant!(HashSet<T> in T);

#[test]
fn insert_failing() {
let mut set: HashSet<i16, BuildHasherDefault<LolHasher>> = Default::default();
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,14 @@
#[macro_use]
extern crate pretty_assertions;

#[macro_use]
mod util;

mod config;
mod nodes;
mod sort;
mod sync;

#[macro_use]
mod util;

#[macro_use]
mod ord;
pub use crate::ord::map as ordmap;
Expand Down
9 changes: 4 additions & 5 deletions src/nodes/btree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ use std::mem;
use std::ops::{Bound, RangeBounds};

use sized_chunks::Chunk;
use typenum::{Add1, Unsigned};

use crate::config::OrdChunkSize as NodeSize;
pub(crate) use crate::config::ORD_CHUNK_SIZE as NODE_SIZE;
use crate::util::{Pool, PoolClone, PoolDefault, PoolRef};

use self::Insert::*;
use self::InsertAction::*;

pub(crate) const NODE_SIZE: usize = NodeSize::USIZE;
const MEDIAN: usize = (NODE_SIZE + 1) >> 1;
const NUM_CHILDREN: usize = NODE_SIZE + 1;

pub trait BTreeValue {
type Key;
Expand All @@ -38,8 +37,8 @@ pub trait BTreeValue {
}

pub(crate) struct Node<A> {
keys: Chunk<A, NodeSize>,
children: Chunk<Option<PoolRef<Node<A>>>, Add1<NodeSize>>,
keys: Chunk<A, NODE_SIZE>,
children: Chunk<Option<PoolRef<Node<A>>>, NUM_CHILDREN>,
}

#[cfg(feature = "pool")]
Expand Down
25 changes: 11 additions & 14 deletions src/nodes/hamt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@ use std::iter::FusedIterator;
use std::slice::{Iter as SliceIter, IterMut as SliceIterMut};
use std::{mem, ptr};

use bitmaps::Bits;
use bitmaps::{Bits, BitsImpl};
use sized_chunks::sparse_chunk::{Iter as ChunkIter, IterMut as ChunkIterMut, SparseChunk};
use typenum::{Pow, Unsigned, U2};

use crate::config::HashLevelSize;
use crate::util::{clone_ref, Pool, PoolClone, PoolDefault, PoolRef, Ref};

pub(crate) type HashWidth = <U2 as Pow<HashLevelSize>>::Output;
pub(crate) type HashBits = <HashWidth as Bits>::Store; // a uint of HASH_SIZE bits
pub(crate) const HASH_SHIFT: usize = HashLevelSize::USIZE;
pub(crate) const HASH_WIDTH: usize = HashWidth::USIZE;
pub(crate) use crate::config::HASH_LEVEL_SIZE as HASH_SHIFT;
pub(crate) const HASH_WIDTH: usize = 2_usize.pow(HASH_SHIFT as u32);
pub(crate) type HashBits = <BitsImpl<HASH_WIDTH> as Bits>::Store; // a uint of HASH_WIDTH bits
pub(crate) const HASH_MASK: HashBits = (HASH_WIDTH - 1) as HashBits;

pub(crate) fn hash_key<K: Hash + ?Sized, S: BuildHasher>(bh: &S, key: &K) -> HashBits {
Expand All @@ -42,7 +39,7 @@ pub trait HashValue {

#[derive(Clone)]
pub(crate) struct Node<A> {
data: SparseChunk<Entry<A>, HashWidth>,
data: SparseChunk<Entry<A>, HASH_WIDTH>,
}

#[allow(unsafe_code)]
Expand All @@ -52,7 +49,7 @@ impl<A> PoolDefault for Node<A> {
SparseChunk::default_uninit(
target
.as_mut_ptr()
.cast::<mem::MaybeUninit<SparseChunk<Entry<A>, HashWidth>>>()
.cast::<mem::MaybeUninit<SparseChunk<Entry<A>, HASH_WIDTH>>>()
.as_mut()
.unwrap(),
)
Expand All @@ -69,7 +66,7 @@ where
self.data.clone_uninit(
target
.as_mut_ptr()
.cast::<mem::MaybeUninit<SparseChunk<Entry<A>, HashWidth>>>()
.cast::<mem::MaybeUninit<SparseChunk<Entry<A>, HASH_WIDTH>>>()
.as_mut()
.unwrap(),
)
Expand Down Expand Up @@ -471,8 +468,8 @@ impl<A: HashValue> CollisionNode<A> {

pub(crate) struct Iter<'a, A> {
count: usize,
stack: Vec<ChunkIter<'a, Entry<A>, HashWidth>>,
current: ChunkIter<'a, Entry<A>, HashWidth>,
stack: Vec<ChunkIter<'a, Entry<A>, HASH_WIDTH>>,
current: ChunkIter<'a, Entry<A>, HASH_WIDTH>,
collision: Option<(HashBits, SliceIter<'a, A>)>,
}

Expand Down Expand Up @@ -551,8 +548,8 @@ impl<'a, A> FusedIterator for Iter<'a, A> where A: 'a {}
pub(crate) struct IterMut<'a, A> {
count: usize,
pool: Pool<Node<A>>,
stack: Vec<ChunkIterMut<'a, Entry<A>, HashWidth>>,
current: ChunkIterMut<'a, Entry<A>, HashWidth>,
stack: Vec<ChunkIterMut<'a, Entry<A>, HASH_WIDTH>>,
current: ChunkIterMut<'a, Entry<A>, HASH_WIDTH>,
collision: Option<(HashBits, SliceIterMut<'a, A>)>,
}

Expand Down
8 changes: 2 additions & 6 deletions src/nodes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ pub(crate) mod hamt;
pub(crate) mod rrb;

pub(crate) mod chunk {
use crate::config::VectorChunkSize;
use sized_chunks as sc;
use typenum::Unsigned;

pub(crate) type Chunk<A> = sc::sized_chunk::Chunk<A, VectorChunkSize>;
pub(crate) const CHUNK_SIZE: usize = VectorChunkSize::USIZE;
pub(crate) use crate::config::VECTOR_CHUNK_SIZE as CHUNK_SIZE;
pub(crate) type Chunk<A> = sized_chunks::sized_chunk::Chunk<A, CHUNK_SIZE>;
}
3 changes: 3 additions & 0 deletions src/ord/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2190,6 +2190,9 @@ mod test {
use ::proptest::num::{i16, usize};
use ::proptest::{bool, collection, proptest};

assert_covariant!(OrdMap<T, i32> in T);
assert_covariant!(OrdMap<i32, T> in T);

#[test]
fn iterates_in_order() {
let map = ordmap! {
Expand Down
2 changes: 2 additions & 0 deletions src/ord/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,8 @@ mod test {
use crate::proptest::*;
use ::proptest::proptest;

assert_covariant!(OrdSet<T> in T);

#[test]
fn match_strings_with_string_slices() {
let mut set: OrdSet<String> = From::from(&ordset!["foo", "bar"]);
Expand Down
21 changes: 7 additions & 14 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

use metrohash::MetroHash64;
use std::hash::{BuildHasher, Hasher};
use std::marker::PhantomData;
use typenum::{Unsigned, U64};

pub(crate) fn is_sorted<A, I>(l: I) -> bool
where
Expand All @@ -22,13 +20,12 @@ where
}
}

pub(crate) struct LolHasher<N: Unsigned = U64> {
pub(crate) struct LolHasher<const N: usize = 64> {
state: u64,
shift: usize,
size: PhantomData<N>,
}

impl<N: Unsigned> LolHasher<N> {
impl<const N: usize> LolHasher<N> {
fn feed_me(&mut self, byte: u8) {
self.state ^= u64::from(byte) << self.shift;
self.shift += 8;
Expand All @@ -38,29 +35,25 @@ impl<N: Unsigned> LolHasher<N> {
}
}

impl<N: Unsigned> Hasher for LolHasher<N> {
impl<const N: usize> Hasher for LolHasher<N> {
fn write(&mut self, bytes: &[u8]) {
for byte in bytes {
self.feed_me(*byte)
}
}

fn finish(&self) -> u64 {
if N::USIZE == 64 {
if N == 64 {
self.state
} else {
self.state & ((1 << N::USIZE) - 1)
self.state & ((1 << N) - 1)
}
}
}

impl<N: Unsigned> Default for LolHasher<N> {
impl<const N: usize> Default for LolHasher<N> {
fn default() -> Self {
LolHasher {
state: 0,
shift: 0,
size: PhantomData,
}
LolHasher { state: 0, shift: 0 }
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,16 @@ macro_rules! def_pool {
}
};
}

#[cfg(test)]
macro_rules! assert_covariant {
($name:ident<$($gen:tt),*> in $param:ident) => {
#[allow(unused_assignments, unused_variables)]
const _: () = {
type Tmp<$param> = $name<$($gen),*>;
fn assign<'a, 'b: 'a>(src: Tmp<&'b i32>, mut dst: Tmp<&'a i32>) {
dst = src;
}
};
}
}
2 changes: 2 additions & 0 deletions src/vector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,8 @@ mod test {
use ::proptest::num::{i32, usize};
use ::proptest::proptest;

assert_covariant!(Vector<T> in T);

#[test]
fn macro_allows_trailing_comma() {
let vec1 = vector![1, 2, 3];
Expand Down

0 comments on commit a2d78ec

Please sign in to comment.