From 618333440944219510d8d427f74fe9cb96965524 Mon Sep 17 00:00:00 2001 From: Neutron Date: Wed, 3 Apr 2024 23:23:37 +0200 Subject: [PATCH 1/9] Initial implementation of FixedMap --- Cargo.lock | 12 ++-- src/fixed_map/mod.rs | 136 ++++++++++++++++++++++++++++++++++++ src/internal/rb_tree/mod.rs | 4 +- src/lib.rs | 1 + src/map/mod.rs | 2 +- 5 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 src/fixed_map/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 885868b..8612e04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "duplicate" @@ -36,9 +36,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -75,9 +75,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] diff --git a/src/fixed_map/mod.rs b/src/fixed_map/mod.rs new file mode 100644 index 0000000..89c015e --- /dev/null +++ b/src/fixed_map/mod.rs @@ -0,0 +1,136 @@ +//! +//! Copyright (C) Warsaw Revamped. Any unauthorized use, modification, or distribution of any portion of this file is prohibited. All rights reserved. +//! + +use crate::allocator::{Allocator, DefaultAllocator}; +use crate::compare::{Compare, Less}; +use crate::fixed_pool::with_overflow::FixedPoolWithOverflow; +use crate::internal::rb_tree::{ + iter::{Iter, IterMut}, + node::Node, +}; +use crate::map::Map; +use duplicate::duplicate_item; +use moveit::{new, New}; +use std::mem::MaybeUninit; +use std::{mem, slice}; + +/// A fixed map which uses the default allocator as an overflow. +pub type DefaultFixedList = + FixedMap; + +#[repr(C)] +#[allow(private_bounds)] +pub struct FixedMap< + K: Eq, + V, + const NODE_COUNT: usize, + OverflowAllocator: Allocator, + C: Compare = Less, +> { + base_map: Map, OverflowAllocator>, C>, + // this should `technically` be conformant - `buffer` should be aligned to the alignment of + // `ListNode`... + buffer: [MaybeUninit>; NODE_COUNT], +} + +#[allow(private_bounds)] +impl + Default> + FixedMap +{ + /// Create a new, empty fixed map. + /// + /// # Arguments + /// `allocator`: The allocator to use + /// + /// # Safety + /// The resulting map must not be moved. + pub unsafe fn new_in(allocator: OverflowAllocator) -> impl New { + new::of(Self { + base_map: Map::with_allocator(FixedPoolWithOverflow::with_allocator(allocator)), + // we actually don't care what the buffer contains + buffer: MaybeUninit::uninit().assume_init(), + }) + .with(|this| { + let this = this.get_unchecked_mut(); + this.base_map + .inner + .allocator + .init(slice::from_raw_parts_mut( + this.buffer.as_mut_ptr().cast(), + this.buffer.len() * mem::size_of::>(), + )); + }) + } +} + +impl> + FixedMap +{ + /// Clears the map, removing all key-value pairs + pub fn clear(&mut self) { + self.base_map.clear() + } + + /// Returns true if the map contains a pair indexed + /// by the given key + /// + /// # Arguments + /// + /// `key`: The key to index the pair + pub fn contains_key(&self, key: &K) -> bool { + self.base_map.contains_key(key) + } + + /// Fetches the value indexed by the key in the map + /// + /// # Arguments + /// + /// `key`: The key to index the pair + pub fn get(&self, key: &K) -> Option<&V> { + self.base_map.get(key) + } + + /// Fetches the value indexed by the key in the map + /// + /// # Arguments + /// + /// `key`: The key to index the pair + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.base_map.get_mut(key) + } + + /// Returns true if the map contains no elements + pub fn is_empty(&self) -> bool { + self.base_map.is_empty() + } + + /// Returns an iterator over the elements in the map. + /// + /// # Safety + /// This iterator is not tested as trees are only partially implemented. + #[duplicate_item( + iter Self Iter; + [iter] [&Self] [Iter]; + [iter_mut] [&mut Self] [IterMut]; + )] + #[allow(clippy::needless_arbitrary_self_type)] + pub unsafe fn iter(self: Self) -> Iter { + self.base_map.iter() + } + + /// Returns the number of elements in the map + pub fn len(&self) -> usize { + self.base_map.len() + } + + /// Removes a key-value pair from the map, + /// returning the pair if it was found + /// + /// # Arguments + /// + /// `key`: The key to index the pair + pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> { + self.base_map.remove_entry(key) + } +} diff --git a/src/internal/rb_tree/mod.rs b/src/internal/rb_tree/mod.rs index 6341c5c..9ce1b48 100644 --- a/src/internal/rb_tree/mod.rs +++ b/src/internal/rb_tree/mod.rs @@ -9,7 +9,7 @@ use std::marker::PhantomData; use self::node::Node; pub mod iter; -mod node; +pub(crate) mod node; #[repr(C)] pub struct RBTree = Less> { @@ -23,7 +23,7 @@ pub struct RBTree = Less> { end: *mut Node, parent: *mut Node, size: u32, - allocator: A, + pub(crate) allocator: A, } impl + Default> RBTree { diff --git a/src/lib.rs b/src/lib.rs index efc7e98..6f313ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod compare; pub mod deque; pub mod equals; pub mod fixed_list; +pub mod fixed_map; mod fixed_pool; pub mod fixed_vector; pub mod hash; diff --git a/src/map/mod.rs b/src/map/mod.rs index 68602ed..519e051 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -13,7 +13,7 @@ use std::fmt::{Debug, Formatter}; /// for those operations #[derive(Default)] pub struct Map = Less> { - inner: RBTree, + pub(crate) inner: RBTree, } impl + Default> Map { From 10ea54ab65584bbf7187cd5da7b6478a8f995223 Mon Sep 17 00:00:00 2001 From: Neutron Date: Thu, 4 Apr 2024 23:15:14 +0200 Subject: [PATCH 2/9] Add comment about usage of fixed_pool_with_overflow instead of fixed_node_pool --- src/fixed_map/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fixed_map/mod.rs b/src/fixed_map/mod.rs index 89c015e..78acbd2 100644 --- a/src/fixed_map/mod.rs +++ b/src/fixed_map/mod.rs @@ -20,7 +20,6 @@ pub type DefaultFixedList = FixedMap; #[repr(C)] -#[allow(private_bounds)] pub struct FixedMap< K: Eq, V, @@ -28,13 +27,15 @@ pub struct FixedMap< OverflowAllocator: Allocator, C: Compare = Less, > { + // real EASTL uses a fixed_node_pool here, which is just fixed_pool_with_overflow templated + // by node size instead of type, so it does not matter and we use fixed_pool_with_overflow + // directly base_map: Map, OverflowAllocator>, C>, // this should `technically` be conformant - `buffer` should be aligned to the alignment of // `ListNode`... buffer: [MaybeUninit>; NODE_COUNT], } -#[allow(private_bounds)] impl + Default> FixedMap { From 2acc2bd334cb84ff5045edd9e82b977c63ea33a2 Mon Sep 17 00:00:00 2001 From: Neutron Date: Sat, 13 Apr 2024 02:33:16 +0200 Subject: [PATCH 3/9] Fixed map fixes, implement both overflow- and non overflow versions, add padding to FixedList, Eq -> PartialEq --- src/equals.rs | 2 +- src/fixed_list/mod.rs | 4 ++- src/fixed_map/mod.rs | 46 +++++++++++++++++++++++--------- src/fixed_pool/mod.rs | 28 +++++++++++-------- src/fixed_pool/with_overflow.rs | 12 ++++----- src/hash_map/entry.rs | 6 ++--- src/hash_map/mod.rs | 23 ++++++++++------ src/hash_set/iter.rs | 6 ++--- src/hash_set/mod.rs | 22 +++++++++------ src/internal/hash_table/entry.rs | 6 ++--- src/internal/hash_table/iter.rs | 22 +++++++-------- src/internal/hash_table/mod.rs | 28 +++++++++++-------- src/internal/hash_table/node.rs | 4 +-- src/internal/rb_tree/mod.rs | 16 ++++++----- src/internal/rb_tree/node.rs | 2 +- src/map/mod.rs | 15 ++++++----- src/set/mod.rs | 8 +++--- src/vector_map.rs | 28 +++++++++---------- 18 files changed, 164 insertions(+), 114 deletions(-) diff --git a/src/equals.rs b/src/equals.rs index 15765e6..fe309fb 100644 --- a/src/equals.rs +++ b/src/equals.rs @@ -17,7 +17,7 @@ pub struct EqualTo { _marker: PhantomData, } -impl Equals for EqualTo { +impl Equals for EqualTo { fn equals(lhs: &T, rhs: &T) -> bool { lhs == rhs } diff --git a/src/fixed_list/mod.rs b/src/fixed_list/mod.rs index 9df1625..b95656a 100644 --- a/src/fixed_list/mod.rs +++ b/src/fixed_list/mod.rs @@ -6,7 +6,7 @@ pub mod conformant; use crate::allocator::{Allocator, DefaultAllocator}; -use crate::fixed_pool::with_overflow::FixedPoolWithOverflow; +use crate::fixed_pool::{with_overflow::FixedPoolWithOverflow, PoolAllocator}; use crate::list::node::{ListNode, ListNodeBase}; use crate::list::List; use moveit::{new, New}; @@ -27,6 +27,8 @@ pub struct FixedList { // this should `technically` be conformant - `buffer` should be aligned to the alignment of // `ListNode`... buffer: [MaybeUninit>; NODE_COUNT], + // ... and then we add an extra node for the padding induced as shown in the conformant version + _pad: MaybeUninit>, } #[allow(private_bounds)] diff --git a/src/fixed_map/mod.rs b/src/fixed_map/mod.rs index 78acbd2..5d64fb0 100644 --- a/src/fixed_map/mod.rs +++ b/src/fixed_map/mod.rs @@ -5,6 +5,7 @@ use crate::allocator::{Allocator, DefaultAllocator}; use crate::compare::{Compare, Less}; use crate::fixed_pool::with_overflow::FixedPoolWithOverflow; +use crate::fixed_pool::{FixedPool, PoolAllocator}; use crate::internal::rb_tree::{ iter::{Iter, IterMut}, node::Node, @@ -15,29 +16,47 @@ use moveit::{new, New}; use std::mem::MaybeUninit; use std::{mem, slice}; -/// A fixed map which uses the default allocator as an overflow. -pub type DefaultFixedList = - FixedMap; +/// A fixed map with overflow which uses the default allocator as an overflow. +#[allow(private_interfaces)] +pub type DefaultFixedMapWithOverflow = + FixedMapWithOverflow; + +/// A fixed map without overflow. +#[allow(private_interfaces)] +pub type FixedMap> = + FixedMapImpl>, C>; + +/// A fixed map with overflow using the given overflow allocator. +#[allow(private_interfaces)] +pub type FixedMapWithOverflow> = + FixedMapImpl, OverflowAllocator>, C>; #[repr(C)] -pub struct FixedMap< - K: Eq, +pub struct FixedMapImpl< + K: PartialEq, V, const NODE_COUNT: usize, - OverflowAllocator: Allocator, + A: Allocator, C: Compare = Less, > { // real EASTL uses a fixed_node_pool here, which is just fixed_pool_with_overflow templated // by node size instead of type, so it does not matter and we use fixed_pool_with_overflow // directly - base_map: Map, OverflowAllocator>, C>, + base_map: Map, // this should `technically` be conformant - `buffer` should be aligned to the alignment of // `ListNode`... buffer: [MaybeUninit>; NODE_COUNT], + // ... and then we add an extra node for the padding induced as shown in the conformant version (of FixedList) + _pad: MaybeUninit>, } -impl + Default> - FixedMap +impl< + K: PartialEq, + V, + const NODE_COUNT: usize, + A: PoolAllocator + Default, + C: Compare + Default, + > FixedMapImpl { /// Create a new, empty fixed map. /// @@ -46,11 +65,12 @@ impl impl New { + pub unsafe fn new() -> impl New { new::of(Self { - base_map: Map::with_allocator(FixedPoolWithOverflow::with_allocator(allocator)), + base_map: Map::with_allocator(A::default()), // we actually don't care what the buffer contains buffer: MaybeUninit::uninit().assume_init(), + _pad: MaybeUninit::uninit().assume_init(), }) .with(|this| { let this = this.get_unchecked_mut(); @@ -65,8 +85,8 @@ impl> - FixedMap +impl> + FixedMapImpl { /// Clears the map, removing all key-value pairs pub fn clear(&mut self) { diff --git a/src/fixed_pool/mod.rs b/src/fixed_pool/mod.rs index bfcb88e..dc101b3 100644 --- a/src/fixed_pool/mod.rs +++ b/src/fixed_pool/mod.rs @@ -4,6 +4,14 @@ use crate::allocator::Allocator; use std::marker::PhantomData; use std::{mem, ptr}; +pub trait PoolAllocator: Allocator { + /// Initializes the fixed pool allocator with the given memory. + /// + /// # Safety + /// `memory` must be a valid chunk of memory, solely owned and managed by the pool allocator. + unsafe fn init(&mut self, memory: &mut [u8]); +} + /// The struct `eastl::fixed_pool_base::Link`. Singly-linked list for memory allocations. pub(crate) struct Link { next: *mut Link, @@ -30,11 +38,15 @@ impl FixedPool { res } - /// Initializes the fixed pool allocator with the given memory. - /// - /// # Safety - /// `memory` must be a valid chunk of memory, solely owned and managed by the pool allocator. - pub unsafe fn init(&mut self, memory: &mut [u8]) { + /// Returns true if the pool can allocate. + #[allow(dead_code)] + pub fn can_allocate(&self) -> bool { + !self.head.is_null() || (self.next != self.capacity) + } +} + +impl PoolAllocator for FixedPool { + unsafe fn init(&mut self, memory: &mut [u8]) { let mut memory_size = memory.len(); let mut node_align = mem::align_of::(); let node_size = mem::size_of::(); @@ -64,12 +76,6 @@ impl FixedPool { self.next = next as *mut Link; self.capacity = (next + memory_size) as *mut Link; } - - /// Returns true if the pool can allocate. - #[allow(dead_code)] - pub fn can_allocate(&self) -> bool { - !self.head.is_null() || (self.next != self.capacity) - } } unsafe impl Allocator for FixedPool { diff --git a/src/fixed_pool/with_overflow.rs b/src/fixed_pool/with_overflow.rs index 07f070f..d5cb777 100644 --- a/src/fixed_pool/with_overflow.rs +++ b/src/fixed_pool/with_overflow.rs @@ -1,5 +1,5 @@ use crate::allocator::Allocator; -use crate::fixed_pool::FixedPool; +use crate::fixed_pool::{FixedPool, PoolAllocator}; use std::{mem, ptr}; /// The class `eastl::fixed_pool_with_overflow`. Attempts to allocate from the pool, @@ -41,12 +41,12 @@ impl FixedPoolWithOverflow bool { self.pool_allocator.can_allocate() } +} - /// Initializes the fixed pool allocator with the given memory. - /// - /// # Safety - /// `memory` must be a valid chunk of memory, solely owned and managed by the pool allocator. - pub unsafe fn init(&mut self, memory: &mut [u8]) { +impl PoolAllocator + for FixedPoolWithOverflow +{ + unsafe fn init(&mut self, memory: &mut [u8]) { self.pool_allocator.init(memory); // store the pool base diff --git a/src/hash_map/entry.rs b/src/hash_map/entry.rs index 57aeecf..bbab513 100644 --- a/src/hash_map/entry.rs +++ b/src/hash_map/entry.rs @@ -4,11 +4,11 @@ use crate::hash::Hash; use crate::internal::hash_table; /// An entry in a hash map. -pub struct Entry<'a, K: Eq, V, A: Allocator, H: Hash, E: Equals>( +pub struct Entry<'a, K: PartialEq, V, A: Allocator, H: Hash, E: Equals>( hash_table::entry::Entry<'a, K, V, A, H, E>, ); -impl<'a, K: Eq, V, A: Allocator, H: Hash, E: Equals> Entry<'a, K, V, A, H, E> { +impl<'a, K: PartialEq, V, A: Allocator, H: Hash, E: Equals> Entry<'a, K, V, A, H, E> { /// Provides in-place mutable access to the value. /// /// # Arguments @@ -37,7 +37,7 @@ impl<'a, K: Eq, V, A: Allocator, H: Hash, E: Equals> Entry<'a, K, V, A, H, } } -impl<'a, K: Eq, V, A: Allocator, H: Hash, E: Equals> +impl<'a, K: PartialEq, V, A: Allocator, H: Hash, E: Equals> From> for Entry<'a, K, V, A, H, E> { fn from(value: hash_table::entry::Entry<'a, K, V, A, H, E>) -> Self { diff --git a/src/hash_map/mod.rs b/src/hash_map/mod.rs index 0c4c171..7b7d558 100644 --- a/src/hash_map/mod.rs +++ b/src/hash_map/mod.rs @@ -19,11 +19,17 @@ pub type DefaultHashMap, E = EqualTo> = /// A hash map that can store and fetch values from a key in O(1) time #[repr(C)] -pub struct HashMap = DefaultHash, E: Equals = EqualTo> { +pub struct HashMap< + K: PartialEq, + V, + A: Allocator, + H: Hash = DefaultHash, + E: Equals = EqualTo, +> { hash_table: HashTable, } -impl HashMap, EqualTo> +impl HashMap, EqualTo> where DefaultHash: Hash, { @@ -35,7 +41,7 @@ where } } -impl, E: Equals> HashMap { +impl, E: Equals> HashMap { /// Clears the hash map, removing all key-value pairs pub fn clear(&mut self) { self.hash_table.clear() @@ -137,7 +143,7 @@ impl, E: Equals> HashMap { } } -impl, E: Equals> Debug +impl, E: Equals> Debug for HashMap { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -152,7 +158,8 @@ impl, E: Equals> Debug } } -impl Default for HashMap, EqualTo> +impl Default + for HashMap, EqualTo> where DefaultHash: Hash, { @@ -161,7 +168,7 @@ where } } -impl FromIterator<(K, V)> +impl FromIterator<(K, V)> for HashMap, EqualTo> where DefaultHash: Hash, @@ -173,11 +180,11 @@ where } } -unsafe impl, E: Equals> Send +unsafe impl, E: Equals> Send for HashMap { } -unsafe impl, E: Equals> Sync +unsafe impl, E: Equals> Sync for HashMap { } diff --git a/src/hash_set/iter.rs b/src/hash_set/iter.rs index c1f7e94..a852fb7 100644 --- a/src/hash_set/iter.rs +++ b/src/hash_set/iter.rs @@ -9,11 +9,11 @@ use crate::internal::hash_table::iter::CompatIter; /// It is unspecified whether or not an element /// inserted after an iterator was created will /// be yielded by the iterator -pub struct Iter<'a, K: Eq + 'a> { +pub struct Iter<'a, K: PartialEq + 'a> { inner: crate::internal::hash_table::iter::Iter<'a, K, ()>, } -impl<'a, K: Eq + 'a> Iter<'a, K> { +impl<'a, K: PartialEq + 'a> Iter<'a, K> { /// Converts the Rust iterator into a pair of /// `(begin, end)` compatibility iterators pub fn into_compat(self) -> (CompatIter<'a, K, ()>, CompatIter<'a, K, ()>) { @@ -51,7 +51,7 @@ impl<'a, K: Eq + 'a> Iter<'a, K> { } } -impl<'a, K: Eq + 'a> Iterator for Iter<'a, K> { +impl<'a, K: PartialEq + 'a> Iterator for Iter<'a, K> { type Item = &'a K; fn next(&mut self) -> Option { diff --git a/src/hash_set/mod.rs b/src/hash_set/mod.rs index 31d980e..c6acb6b 100644 --- a/src/hash_set/mod.rs +++ b/src/hash_set/mod.rs @@ -16,11 +16,16 @@ pub type DefaultHashSet, E = EqualTo> = HashSet = DefaultHash, E: Equals = EqualTo> { +pub struct HashSet< + K: PartialEq, + A: Allocator, + H: Hash = DefaultHash, + E: Equals = EqualTo, +> { hash_table: HashTable, } -impl HashSet, EqualTo> +impl HashSet, EqualTo> where DefaultHash: Hash, { @@ -32,7 +37,7 @@ where } } -impl, E: Equals> HashSet { +impl, E: Equals> HashSet { /// Clears the hash set, removing all keys pub fn clear(&mut self) { self.hash_table.clear() @@ -103,7 +108,7 @@ impl, E: Equals> HashSet { } } -impl, E: Equals> Debug for HashSet { +impl, E: Equals> Debug for HashSet { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, @@ -116,7 +121,7 @@ impl, E: Equals> Debug for HashSet Default for HashSet, EqualTo> +impl Default for HashSet, EqualTo> where DefaultHash: Hash, { @@ -125,7 +130,8 @@ where } } -impl FromIterator for HashSet, EqualTo> +impl FromIterator + for HashSet, EqualTo> where DefaultHash: Hash, { @@ -136,11 +142,11 @@ where } } -unsafe impl, E: Equals> Send +unsafe impl, E: Equals> Send for HashSet { } -unsafe impl, E: Equals> Sync +unsafe impl, E: Equals> Sync for HashSet { } diff --git a/src/internal/hash_table/entry.rs b/src/internal/hash_table/entry.rs index 1d1c33a..67b4264 100644 --- a/src/internal/hash_table/entry.rs +++ b/src/internal/hash_table/entry.rs @@ -5,21 +5,21 @@ use crate::internal::hash_table::node::Node; use crate::internal::hash_table::HashTable; /// A vacant node - one that has not been inserted yet. -pub struct VacantEntry<'a, K: Eq, V, A: Allocator, H: Hash, E: Equals> { +pub struct VacantEntry<'a, K: PartialEq, V, A: Allocator, H: Hash, E: Equals> { pub(crate) table: &'a mut HashTable, pub(crate) target_bucket: &'a mut *mut Node, pub(crate) key: K, } /// An entry in a hash table. -pub enum Entry<'a, K: Eq, V, A: Allocator, H: Hash, E: Equals> { +pub enum Entry<'a, K: PartialEq, V, A: Allocator, H: Hash, E: Equals> { /// There was a node found already for the key. Occupied(&'a mut Node), /// There was not a node already present for the key. Vacant(VacantEntry<'a, K, V, A, H, E>), } -impl<'a, K: Eq, V, A: Allocator, H: Hash, E: Equals> Entry<'a, K, V, A, H, E> { +impl<'a, K: PartialEq, V, A: Allocator, H: Hash, E: Equals> Entry<'a, K, V, A, H, E> { /// Provides in-place mutable access to the value. /// /// # Arguments diff --git a/src/internal/hash_table/iter.rs b/src/internal/hash_table/iter.rs index c9221e6..5b49303 100644 --- a/src/internal/hash_table/iter.rs +++ b/src/internal/hash_table/iter.rs @@ -6,7 +6,7 @@ use std::marker::PhantomData; /// rust, so these are strictly for compatibility's /// sake. They cannot actually be used to iterate. #[repr(C)] -pub struct CompatIter<'a, K: Eq + 'a, V: 'a> { +pub struct CompatIter<'a, K: PartialEq + 'a, V: 'a> { node_ptr: *mut Node, bucket_ptr: *const *const Node, _marker: PhantomData<&'a (K, V)>, @@ -17,7 +17,7 @@ pub struct CompatIter<'a, K: Eq + 'a, V: 'a> { /// rust, so these are strictly for compatibility's /// sake. They cannot actually be used to iterate. #[repr(C)] -pub struct CompatIterMut<'a, K: Eq + 'a, V: 'a> { +pub struct CompatIterMut<'a, K: PartialEq + 'a, V: 'a> { node_ptr: *mut Node, bucket_ptr: *const *mut Node, _marker: PhantomData<&'a (K, V)>, @@ -36,12 +36,12 @@ pub struct CompatIterMut<'a, K: Eq + 'a, V: 'a> { /// inserted after an iterator was created will /// be yielded by the iterator #[derive(Clone)] -struct RawIter<'a, K: Eq + 'a, V: 'a> { +struct RawIter<'a, K: PartialEq + 'a, V: 'a> { bucket_iter: std::slice::Iter<'a, *mut Node>, node_ptr: *mut Node, } -impl<'a, K: Eq, V> RawIter<'a, K, V> { +impl<'a, K: PartialEq, V> RawIter<'a, K, V> { /// Converts the Rust iterator into a pair of /// `(begin, end)` compatibility iterators fn into_compat(self) -> (CompatIter<'a, K, V>, CompatIter<'a, K, V>) { @@ -162,7 +162,7 @@ impl<'a, K: Eq, V> RawIter<'a, K, V> { } } -impl<'a, K: Eq, V> Iterator for RawIter<'a, K, V> { +impl<'a, K: PartialEq, V> Iterator for RawIter<'a, K, V> { type Item = (&'a K, &'a mut V); fn next(&mut self) -> Option { @@ -196,11 +196,11 @@ impl<'a, K: Eq, V> Iterator for RawIter<'a, K, V> { /// inserted after an iterator was created will /// be yielded by the iterator #[derive(Clone)] -pub struct Iter<'a, K: Eq + 'a, V: 'a> { +pub struct Iter<'a, K: PartialEq + 'a, V: 'a> { inner: RawIter<'a, K, V>, } -impl<'a, K: Eq + 'a, V: 'a> Iter<'a, K, V> { +impl<'a, K: PartialEq + 'a, V: 'a> Iter<'a, K, V> { /// Converts the Rust iterator into a pair of /// `(begin, end)` compatibility iterators pub fn into_compat(self) -> (CompatIter<'a, K, V>, CompatIter<'a, K, V>) { @@ -259,7 +259,7 @@ impl<'a, K: Eq + 'a, V: 'a> Iter<'a, K, V> { } } -impl<'a, K: Eq + 'a, V: 'a> Iterator for Iter<'a, K, V> { +impl<'a, K: PartialEq + 'a, V: 'a> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); fn next(&mut self) -> Option { @@ -281,11 +281,11 @@ impl<'a, K: Eq + 'a, V: 'a> Iterator for Iter<'a, K, V> { /// inserted after an iterator was created will /// be yielded by the iterator #[derive(Clone)] -pub struct IterMut<'a, K: Eq + 'a, V: 'a> { +pub struct IterMut<'a, K: PartialEq + 'a, V: 'a> { inner: RawIter<'a, K, V>, } -impl<'a, K: Eq + 'a, V: 'a> IterMut<'a, K, V> { +impl<'a, K: PartialEq + 'a, V: 'a> IterMut<'a, K, V> { /// Converts the Rust iterator into a pair of /// `(begin, end)` compatibility iterators pub fn into_compat(self) -> (CompatIter<'a, K, V>, CompatIter<'a, K, V>) { @@ -331,7 +331,7 @@ impl<'a, K: Eq + 'a, V: 'a> IterMut<'a, K, V> { } } -impl<'a, K: Eq + 'a, V: 'a> Iterator for IterMut<'a, K, V> { +impl<'a, K: PartialEq + 'a, V: 'a> Iterator for IterMut<'a, K, V> { type Item = (&'a K, &'a mut V); fn next(&mut self) -> Option { diff --git a/src/internal/hash_table/mod.rs b/src/internal/hash_table/mod.rs index e4807e9..c67b42b 100644 --- a/src/internal/hash_table/mod.rs +++ b/src/internal/hash_table/mod.rs @@ -27,8 +27,13 @@ pub type DefaultHashTable, E = EqualTo> = /// A base hashtable used to support hash maps and sets #[repr(C)] -pub struct HashTable = DefaultHash, E: Equals = EqualTo> -{ +pub struct HashTable< + K: PartialEq, + V, + A: Allocator, + H: Hash = DefaultHash, + E: Equals = EqualTo, +> { /// The C++ object has some key extractor functor here /// that we don't need _pad: u8, @@ -43,7 +48,7 @@ pub struct HashTable = DefaultHash, E: Equ /// Two entries - a null entry and the sentinel. static EMPTY_BUCKET_ARR: [usize; 2] = [0, !0]; -impl HashTable, EqualTo> +impl HashTable, EqualTo> where DefaultHash: Hash, { @@ -53,7 +58,7 @@ where } } -impl, E: Equals> HashTable { +impl, E: Equals> HashTable { /// Clears the hash table, removing all key-value pairs pub fn clear(&mut self) { self.free_buckets(); @@ -403,7 +408,8 @@ impl, E: Equals> HashTable } } -impl Default for HashTable, EqualTo> +impl Default + for HashTable, EqualTo> where DefaultHash: Hash, { @@ -412,13 +418,13 @@ where } } -impl, E: Equals> Drop for HashTable { +impl, E: Equals> Drop for HashTable { fn drop(&mut self) { self.free_buckets(); } } -impl FromIterator<(K, V)> +impl FromIterator<(K, V)> for HashTable, EqualTo> where DefaultHash: Hash, @@ -432,11 +438,11 @@ where } } -unsafe impl, E: Equals> Send +unsafe impl, E: Equals> Send for HashTable { } -unsafe impl, E: Equals> Sync +unsafe impl, E: Equals> Sync for HashTable { } @@ -555,7 +561,7 @@ mod test { self.a == other.a } } - impl<'a> Eq for Test<'a> {} + impl<'a> PartialEq for Test<'a> {} impl<'a> Hash> for DefaultHash> { fn hash(val: &Test<'a>) -> usize { @@ -581,7 +587,7 @@ mod test { assert_eq!(bag, 2); } - #[derive(Debug, PartialEq, Eq)] + #[derive(Debug, PartialEq, PartialEq)] struct A { a: u32, } diff --git a/src/internal/hash_table/node.rs b/src/internal/hash_table/node.rs index 63e691e..186ef2f 100644 --- a/src/internal/hash_table/node.rs +++ b/src/internal/hash_table/node.rs @@ -1,12 +1,12 @@ /// A node in a hashtable #[repr(C)] -pub struct Node { +pub struct Node { pub key: K, pub val: V, pub next: *mut Self, } -impl Node { +impl Node { /// Creates the node from a key/value /// /// # Arguments diff --git a/src/internal/rb_tree/mod.rs b/src/internal/rb_tree/mod.rs index 9ce1b48..b7b1936 100644 --- a/src/internal/rb_tree/mod.rs +++ b/src/internal/rb_tree/mod.rs @@ -12,7 +12,7 @@ pub mod iter; pub(crate) mod node; #[repr(C)] -pub struct RBTree = Less> { +pub struct RBTree = Less> { /// A 1-size functor in C++ compare: C, /// Real EASTL uses a node without a K/V pair @@ -26,7 +26,7 @@ pub struct RBTree = Less> { pub(crate) allocator: A, } -impl + Default> RBTree { +impl + Default> RBTree { /// Returns a default new red-black tree fn new() -> Self { Self { @@ -40,19 +40,21 @@ impl + Default> RBTree + Default> Default for RBTree { +impl + Default> Default + for RBTree +{ fn default() -> Self { Self::new() } } -impl> Drop for RBTree { +impl> Drop for RBTree { fn drop(&mut self) { self.free_nodes() } } -impl + Default> RBTree { +impl + Default> RBTree { /// Constructs a red-black tree using a specified allocator /// /// # Arguments @@ -70,7 +72,7 @@ impl + Default> RBTree { } } -impl> RBTree { +impl> RBTree { /// Constructs a red-black tree using a specified comparator /// /// # Arguments @@ -88,7 +90,7 @@ impl> RBTree { } } -impl> RBTree { +impl> RBTree { /// Constructs a red-black tree using a specified allocator /// and comparator /// diff --git a/src/internal/rb_tree/node.rs b/src/internal/rb_tree/node.rs index 28d8171..dbf5e91 100644 --- a/src/internal/rb_tree/node.rs +++ b/src/internal/rb_tree/node.rs @@ -3,7 +3,7 @@ use std::{fmt::Debug, marker::PhantomData, ptr}; /// The color of a red-black tree node #[repr(C)] -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Eq, PartialEq)] pub(crate) enum Color { Red = 0, Black = 1, diff --git a/src/map/mod.rs b/src/map/mod.rs index 519e051..b38e482 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -12,11 +12,12 @@ use std::fmt::{Debug, Formatter}; /// need ordering, look at `HashMap`, which takes O(1) time /// for those operations #[derive(Default)] -pub struct Map = Less> { +#[repr(C)] +pub struct Map = Less> { pub(crate) inner: RBTree, } -impl + Default> Map { +impl + Default> Map { /// Constructs a map using a specified allocator /// /// # Arguments @@ -29,7 +30,7 @@ impl + Default> Map { } } -impl> Map { +impl> Map { /// Constructs a map using a specified comparator /// /// # Arguments @@ -42,7 +43,7 @@ impl> Map { } } -impl> Map { +impl> Map { /// Constructs a map using a specified allocator /// and comparator /// @@ -147,7 +148,7 @@ impl> Map { } } -impl> Debug for Map { +impl> Debug for Map { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, @@ -160,12 +161,12 @@ impl> Debug for Map + Send> Send +unsafe impl + Send> Send for Map { } -unsafe impl + Sync> Sync +unsafe impl + Sync> Sync for Map { } diff --git a/src/set/mod.rs b/src/set/mod.rs index e0bfdaa..676ba9e 100644 --- a/src/set/mod.rs +++ b/src/set/mod.rs @@ -9,11 +9,11 @@ use crate::{ /// need ordering, look at `HashSet`, which takes O(1) time /// for those operations #[derive(Default)] -pub struct Set = Less> { +pub struct Set = Less> { inner: RBTree, } -impl + Default> Set { +impl + Default> Set { /// Constructs a set using a specified allocator /// /// # Arguments @@ -26,7 +26,7 @@ impl + Default> Set { } } -impl> Set { +impl> Set { /// Constructs a set using a specified comparator /// /// # Arguments @@ -39,7 +39,7 @@ impl> Set { } } -impl> Set { +impl> Set { /// Constructs a set using a specified allocator /// and comparator /// diff --git a/src/vector_map.rs b/src/vector_map.rs index d63cc1e..d4b9e08 100644 --- a/src/vector_map.rs +++ b/src/vector_map.rs @@ -11,12 +11,12 @@ pub type DefaultVectorMap> = VectorMap = Less> { +pub struct VectorMap = Less> { base: Vector<(K, V), A>, _compare: C, } -impl VectorMap> { +impl VectorMap> { /// Creates a new empty vector map pub fn new() -> Self { Self { @@ -38,7 +38,7 @@ impl VectorMap> } } -impl + Default> VectorMap { +impl + Default> VectorMap { /// Returns the capacity of the vector map pub fn capacity(&self) -> usize { self.base.capacity() @@ -185,13 +185,13 @@ impl + Default> VectorMap { } } -impl> AsRef<[(K, V)]> for VectorMap { +impl> AsRef<[(K, V)]> for VectorMap { fn as_ref(&self) -> &[(K, V)] { self.base.as_ref() } } -impl> Debug for VectorMap { +impl> Debug for VectorMap { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, @@ -205,13 +205,13 @@ impl> Debug for VectorMap Default for VectorMap> { +impl Default for VectorMap> { fn default() -> Self { Self::new() } } -impl> Deref for VectorMap { +impl> Deref for VectorMap { type Target = [(K, V)]; fn deref(&self) -> &Self::Target { @@ -219,7 +219,7 @@ impl> Deref for VectorMap From<&[(K, V)]> +impl From<&[(K, V)]> for VectorMap> { fn from(value: &[(K, V)]) -> Self { @@ -231,7 +231,7 @@ impl From<&[(K, V) } } -impl From<&mut [(K, V)]> +impl From<&mut [(K, V)]> for VectorMap> { fn from(value: &mut [(K, V)]) -> Self { @@ -239,7 +239,7 @@ impl From<&mut [(K } } -impl From<[(K, V); N]> +impl From<[(K, V); N]> for VectorMap> { fn from(value: [(K, V); N]) -> Self { @@ -251,7 +251,7 @@ impl From<[(K, V) } } -impl +impl From<&[(K, V); N]> for VectorMap> { fn from(value: &[(K, V); N]) -> Self { @@ -259,7 +259,7 @@ impl FromIterator<(K, V)> +impl FromIterator<(K, V)> for VectorMap> { fn from_iter>(iter: T) -> Self { @@ -272,11 +272,11 @@ impl FromIterator<(K, V)> } } -unsafe impl + Send> Send +unsafe impl + Send> Send for VectorMap { } -unsafe impl + Sync> Sync +unsafe impl + Sync> Sync for VectorMap { } From 0a7fb0bd5f711c6db585a642cd50334e6f454db1 Mon Sep 17 00:00:00 2001 From: Neutron Date: Sat, 13 Apr 2024 02:35:05 +0200 Subject: [PATCH 4/9] Fix _pad on fixed_list --- src/fixed_list/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fixed_list/mod.rs b/src/fixed_list/mod.rs index b95656a..8a8c0c4 100644 --- a/src/fixed_list/mod.rs +++ b/src/fixed_list/mod.rs @@ -52,6 +52,7 @@ impl }, // we actually don't care what the buffer contains buffer: MaybeUninit::uninit().assume_init(), + _pad: MaybeUninit::uninit().assume_init(), }) .with(|this| { let this = this.get_unchecked_mut(); From 475c0dba763d5bf1406b12c6b68f60451d7a1a69 Mon Sep 17 00:00:00 2001 From: Neutron Date: Sat, 13 Apr 2024 02:36:48 +0200 Subject: [PATCH 5/9] Expose implementation details because I can't not --- src/fixed_map/mod.rs | 3 --- src/internal/rb_tree/node.rs | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/fixed_map/mod.rs b/src/fixed_map/mod.rs index 5d64fb0..f189be8 100644 --- a/src/fixed_map/mod.rs +++ b/src/fixed_map/mod.rs @@ -17,17 +17,14 @@ use std::mem::MaybeUninit; use std::{mem, slice}; /// A fixed map with overflow which uses the default allocator as an overflow. -#[allow(private_interfaces)] pub type DefaultFixedMapWithOverflow = FixedMapWithOverflow; /// A fixed map without overflow. -#[allow(private_interfaces)] pub type FixedMap> = FixedMapImpl>, C>; /// A fixed map with overflow using the given overflow allocator. -#[allow(private_interfaces)] pub type FixedMapWithOverflow> = FixedMapImpl, OverflowAllocator>, C>; diff --git a/src/internal/rb_tree/node.rs b/src/internal/rb_tree/node.rs index dbf5e91..0f36fe1 100644 --- a/src/internal/rb_tree/node.rs +++ b/src/internal/rb_tree/node.rs @@ -4,7 +4,7 @@ use std::{fmt::Debug, marker::PhantomData, ptr}; /// The color of a red-black tree node #[repr(C)] #[derive(Debug, Eq, PartialEq)] -pub(crate) enum Color { +pub enum Color { Red = 0, Black = 1, } @@ -22,7 +22,7 @@ impl From for Color { /// A parent-color compressed pair. Combined, the /// pair takes `std::mem::size_of::()` bytes #[repr(C)] -pub(crate) struct ParentColor { +pub struct ParentColor { raw_ptr: usize, _ignore_key: PhantomData, _ignore_value: PhantomData, @@ -84,7 +84,7 @@ impl Default for ParentColor { #[repr(C)] #[derive(Debug)] -pub(crate) struct Node { +pub struct Node { pub right: *mut Node, pub left: *mut Node, pub parent: ParentColor, From d3398403ec78d7eba4dc4f2f17462e08a81fdc82 Mon Sep 17 00:00:00 2001 From: Neutron Date: Sat, 13 Apr 2024 18:38:02 +0200 Subject: [PATCH 6/9] Fix Eq -> PartialEq changes --- src/internal/hash_table/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal/hash_table/mod.rs b/src/internal/hash_table/mod.rs index c67b42b..5ea553b 100644 --- a/src/internal/hash_table/mod.rs +++ b/src/internal/hash_table/mod.rs @@ -561,7 +561,7 @@ mod test { self.a == other.a } } - impl<'a> PartialEq for Test<'a> {} + impl<'a> Eq for Test<'a> {} impl<'a> Hash> for DefaultHash> { fn hash(val: &Test<'a>) -> usize { @@ -587,7 +587,7 @@ mod test { assert_eq!(bag, 2); } - #[derive(Debug, PartialEq, PartialEq)] + #[derive(Debug, Eq, PartialEq)] struct A { a: u32, } From 2d3a6a8ade07dfbbb83b2e6fcb3372d6adebf1c5 Mon Sep 17 00:00:00 2001 From: Neutron Date: Sat, 13 Apr 2024 18:47:34 +0200 Subject: [PATCH 7/9] Fix test for fixed_list to account for added extra padding --- src/fixed_list/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixed_list/mod.rs b/src/fixed_list/mod.rs index 8a8c0c4..aae4e4e 100644 --- a/src/fixed_list/mod.rs +++ b/src/fixed_list/mod.rs @@ -207,7 +207,7 @@ mod test { mem::size_of::() + mem::size_of::() + mem::size_of::, DefaultAllocator>>() - + mem::size_of::>() + + mem::size_of::>() * 2 ); } From 28bbf2be072812672e023a57e009e3fe27cea123 Mon Sep 17 00:00:00 2001 From: Neutron Date: Sat, 13 Apr 2024 18:54:05 +0200 Subject: [PATCH 8/9] Update readme, remove half-obsolete comment --- README.md | 8 +++++++- src/fixed_vector/mod.rs | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0902ae4..899bc7f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # EASTL-rs + A binary-compatible EASTL implementation written in Rust [Crate](https://crates.io/crates/eastl-rs) | [Documentation](https://docs.rs/crate/eastl-rs) ## Fully implemented containers* + - `HashMap` - `HashSet` - `String` @@ -11,16 +13,20 @@ A binary-compatible EASTL implementation written in Rust - `Deque` - `Queue` - `VectorMap` -- `FixedVector` +- `FixedVector` (only with overflow enabled) - `List` * there might be some things missing :) ## Partially implemented containers + - `Map` (traversal only) - `Set` (traversal only) +- `FixedList` (not fully conformant on stable, only with overflow enabled) +- `FixedMap` (only non-conformant version implemented) ## Planned containers + - `HashMultimap` - `HashMultiset` - `Map` diff --git a/src/fixed_vector/mod.rs b/src/fixed_vector/mod.rs index 4179770..3e9936b 100644 --- a/src/fixed_vector/mod.rs +++ b/src/fixed_vector/mod.rs @@ -13,8 +13,6 @@ use std::{mem, ptr}; mod allocator; -/// TODO overflow_enabled, more than traversal? - /// Fixed vector with the default allocator. pub type DefaultFixedVector = FixedVector; From 4bce48368fb47db658262f1a4ad7c3c75a97c5a1 Mon Sep 17 00:00:00 2001 From: Neutron Date: Sat, 13 Apr 2024 18:54:39 +0200 Subject: [PATCH 9/9] Bump to 0.14 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5fb3b1..87d4793 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,7 @@ dependencies = [ [[package]] name = "eastl-rs" -version = "0.13.2" +version = "0.14.0" dependencies = [ "duplicate", "itertools", diff --git a/Cargo.toml b/Cargo.toml index 5c34f3c..010a976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "eastl-rs" authors = ["Andrew Buck"] description = "EASTL binary-compatible Rust implementations" documentation = "https://docs.rs/crate/eastl-rs" -version = "0.13.2" +version = "0.14.0" edition = "2021" license-file = "LICENSE" readme = "README.md"