diff --git a/CHANGELOG.md b/CHANGELOG.md index edcf429..6700a0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Cargo.toml b/Cargo.toml index 93f581d..9415bf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/src/config.rs b/src/config.rs index f861139..6abec59 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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 diff --git a/src/hash/map.rs b/src/hash/map.rs index 4bbba40..8645f7a 100644 --- a/src/hash/map.rs +++ b/src/hash/map.rs @@ -2125,6 +2125,9 @@ mod test { use ::proptest::{collection, proptest}; use std::hash::BuildHasherDefault; + assert_covariant!(HashMap in T); + assert_covariant!(HashMap in T); + #[test] fn safe_mutation() { let v1: HashMap = (0..131_072).map(|i| (i, i)).collect::>(); diff --git a/src/hash/set.rs b/src/hash/set.rs index 11ba3a0..0d67207 100644 --- a/src/hash/set.rs +++ b/src/hash/set.rs @@ -1070,6 +1070,8 @@ mod test { use ::proptest::proptest; use std::hash::BuildHasherDefault; + assert_covariant!(HashSet in T); + #[test] fn insert_failing() { let mut set: HashSet> = Default::default(); diff --git a/src/lib.rs b/src/lib.rs index 0444dbd..ed318f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/nodes/btree.rs b/src/nodes/btree.rs index 84f63fa..0102a18 100644 --- a/src/nodes/btree.rs +++ b/src/nodes/btree.rs @@ -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; @@ -38,8 +37,8 @@ pub trait BTreeValue { } pub(crate) struct Node { - keys: Chunk, - children: Chunk>>, Add1>, + keys: Chunk, + children: Chunk>>, NUM_CHILDREN>, } #[cfg(feature = "pool")] diff --git a/src/nodes/hamt.rs b/src/nodes/hamt.rs index 945068b..9bf75fa 100644 --- a/src/nodes/hamt.rs +++ b/src/nodes/hamt.rs @@ -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 = >::Output; -pub(crate) type HashBits = ::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 = 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(bh: &S, key: &K) -> HashBits { @@ -42,7 +39,7 @@ pub trait HashValue { #[derive(Clone)] pub(crate) struct Node { - data: SparseChunk, HashWidth>, + data: SparseChunk, HASH_WIDTH>, } #[allow(unsafe_code)] @@ -52,7 +49,7 @@ impl PoolDefault for Node { SparseChunk::default_uninit( target .as_mut_ptr() - .cast::, HashWidth>>>() + .cast::, HASH_WIDTH>>>() .as_mut() .unwrap(), ) @@ -69,7 +66,7 @@ where self.data.clone_uninit( target .as_mut_ptr() - .cast::, HashWidth>>>() + .cast::, HASH_WIDTH>>>() .as_mut() .unwrap(), ) @@ -471,8 +468,8 @@ impl CollisionNode { pub(crate) struct Iter<'a, A> { count: usize, - stack: Vec, HashWidth>>, - current: ChunkIter<'a, Entry, HashWidth>, + stack: Vec, HASH_WIDTH>>, + current: ChunkIter<'a, Entry, HASH_WIDTH>, collision: Option<(HashBits, SliceIter<'a, A>)>, } @@ -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>, - stack: Vec, HashWidth>>, - current: ChunkIterMut<'a, Entry, HashWidth>, + stack: Vec, HASH_WIDTH>>, + current: ChunkIterMut<'a, Entry, HASH_WIDTH>, collision: Option<(HashBits, SliceIterMut<'a, A>)>, } diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 24e7455..428bfc7 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -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 = sc::sized_chunk::Chunk; - pub(crate) const CHUNK_SIZE: usize = VectorChunkSize::USIZE; + pub(crate) use crate::config::VECTOR_CHUNK_SIZE as CHUNK_SIZE; + pub(crate) type Chunk = sized_chunks::sized_chunk::Chunk; } diff --git a/src/ord/map.rs b/src/ord/map.rs index 1c32a38..3e6895a 100644 --- a/src/ord/map.rs +++ b/src/ord/map.rs @@ -2190,6 +2190,9 @@ mod test { use ::proptest::num::{i16, usize}; use ::proptest::{bool, collection, proptest}; + assert_covariant!(OrdMap in T); + assert_covariant!(OrdMap in T); + #[test] fn iterates_in_order() { let map = ordmap! { diff --git a/src/ord/set.rs b/src/ord/set.rs index 2fd674a..91a5bd9 100644 --- a/src/ord/set.rs +++ b/src/ord/set.rs @@ -1185,6 +1185,8 @@ mod test { use crate::proptest::*; use ::proptest::proptest; + assert_covariant!(OrdSet in T); + #[test] fn match_strings_with_string_slices() { let mut set: OrdSet = From::from(&ordset!["foo", "bar"]); diff --git a/src/test.rs b/src/test.rs index 9887d01..5762bca 100644 --- a/src/test.rs +++ b/src/test.rs @@ -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(l: I) -> bool where @@ -22,13 +20,12 @@ where } } -pub(crate) struct LolHasher { +pub(crate) struct LolHasher { state: u64, shift: usize, - size: PhantomData, } -impl LolHasher { +impl LolHasher { fn feed_me(&mut self, byte: u8) { self.state ^= u64::from(byte) << self.shift; self.shift += 8; @@ -38,7 +35,7 @@ impl LolHasher { } } -impl Hasher for LolHasher { +impl Hasher for LolHasher { fn write(&mut self, bytes: &[u8]) { for byte in bytes { self.feed_me(*byte) @@ -46,21 +43,17 @@ impl Hasher for LolHasher { } 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 Default for LolHasher { +impl Default for LolHasher { fn default() -> Self { - LolHasher { - state: 0, - shift: 0, - size: PhantomData, - } + LolHasher { state: 0, shift: 0 } } } diff --git a/src/util.rs b/src/util.rs index 5451f15..c5d6f59 100644 --- a/src/util.rs +++ b/src/util.rs @@ -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; + } + }; + } +} diff --git a/src/vector/mod.rs b/src/vector/mod.rs index 96170d3..a69d289 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -2270,6 +2270,8 @@ mod test { use ::proptest::num::{i32, usize}; use ::proptest::proptest; + assert_covariant!(Vector in T); + #[test] fn macro_allows_trailing_comma() { let vec1 = vector![1, 2, 3];