From cdca6fba5eba52dcd54e97dc34b325a76aa06186 Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Sun, 17 May 2020 21:09:42 +0300 Subject: [PATCH 01/11] Reduce size_of - `data` field is removed, instead the allocation layout is changed such that first element of control bytes start exactly at one past last element of data table. So we just use negative index to access data table without touching size field. - Rewritten `calculate_layout` to remove padding between two tables, its placed either at start or end of allocation if necessary. --- src/raw/mod.rs | 113 ++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index bb4412b378..05000b201d 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -2,6 +2,7 @@ use crate::alloc::alloc::{alloc, dealloc, handle_alloc_error}; use crate::scopeguard::guard; use crate::CollectionAllocErr; use core::alloc::Layout; +use core::cmp; use core::hint; use core::iter::FusedIterator; use core::marker::PhantomData; @@ -208,47 +209,34 @@ fn bucket_mask_to_capacity(bucket_mask: usize) -> usize { } } +#[cfg_attr(feature = "inline-more", inline)] +fn align_size(size: usize, align: usize) -> Option { + Some(size.checked_add(align - 1)? & !(align - 1)) +} + // Returns a Layout which describes the allocation required for a hash table, -// and the offset of the buckets in the allocation. +// and the offset of the control bytes in the allocation. +// (the offset is also one past last element of data table) /// /// Returns `None` if an overflow occurs. #[cfg_attr(feature = "inline-more", inline)] -#[cfg(feature = "nightly")] fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { debug_assert!(buckets.is_power_of_two()); - // Array of buckets - let data = Layout::array::(buckets).ok()?; + let align = cmp::max(mem::align_of::(), Group::WIDTH); - // Array of control bytes. This must be aligned to the group size. - // - // We add `Group::WIDTH` control bytes at the end of the array which - // replicate the bytes at the start of the array and thus avoids the need to - // perform bounds-checking while probing. - // - // There is no possible overflow here since buckets is a power of two and - // Group::WIDTH is a small number. - let ctrl = unsafe { Layout::from_size_align_unchecked(buckets + Group::WIDTH, Group::WIDTH) }; + unsafe { + let data = Layout::from_size_align_unchecked( + align_size(mem::size_of::() * buckets, align)?, align); - ctrl.extend(data).ok() -} + let ctrl = Layout::from_size_align_unchecked( + align_size(buckets + Group::WIDTH, align)?, align); -// Returns a Layout which describes the allocation required for a hash table, -// and the offset of the buckets in the allocation. -#[cfg_attr(feature = "inline-more", inline)] -#[cfg(not(feature = "nightly"))] -fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { - debug_assert!(buckets.is_power_of_two()); + let layout = Layout::from_size_align_unchecked( + data.size().checked_add(ctrl.size())?, align); - // Manual layout calculation since Layout methods are not yet stable. - let data_align = usize::max(mem::align_of::(), Group::WIDTH); - let data_offset = (buckets + Group::WIDTH).checked_add(data_align - 1)? & !(data_align - 1); - let len = data_offset.checked_add(mem::size_of::().checked_mul(buckets)?)?; - - Some(( - unsafe { Layout::from_size_align_unchecked(len, data_align) }, - data_offset, - )) + Some((layout, /*ctrl offset*/ layout.size() - ctrl.size())) + } } /// A reference to a hash table bucket containing a `T`. @@ -279,7 +267,9 @@ impl Bucket { // won't overflow because index must be less than length (index + 1) as *mut T } else { - base.as_ptr().add(index) + // the pointer arithmetic below might cross allocation bounds + // because RawTable::iter() could call this function with empty table and index=0 + base.as_ptr().wrapping_offset(!index as isize) }; Self { ptr: NonNull::new_unchecked(ptr), @@ -290,7 +280,8 @@ impl Bucket { if mem::size_of::() == 0 { self.ptr.as_ptr() as usize - 1 } else { - offset_from(self.ptr.as_ptr(), base.as_ptr()) + //emulation of wrapping_offset_from - currently available only at nightly + (base.as_ptr() as usize - self.ptr.as_ptr() as usize) / mem::size_of::() - 1 } } #[cfg_attr(feature = "inline-more", inline)] @@ -303,11 +294,11 @@ impl Bucket { } } #[cfg_attr(feature = "inline-more", inline)] - unsafe fn add(&self, offset: usize) -> Self { + unsafe fn next_n(&self, offset: usize) -> Self { let ptr = if mem::size_of::() == 0 { (self.ptr.as_ptr() as usize + offset) as *mut T } else { - self.ptr.as_ptr().add(offset) + self.ptr.as_ptr().sub(offset) }; Self { ptr: NonNull::new_unchecked(ptr), @@ -345,12 +336,10 @@ pub struct RawTable { // number of buckets in the table. bucket_mask: usize, - // Pointer to the array of control bytes + // [Padding], .., T1, T2, ..., C1, C2, .., [Padding] + // ^ points here ctrl: NonNull, - // Pointer to the array of buckets - data: NonNull, - // Number of elements that can be inserted before we need to grow the table growth_left: usize, @@ -370,7 +359,6 @@ impl RawTable { #[cfg_attr(feature = "inline-more", inline)] pub fn new() -> Self { Self { - data: NonNull::dangling(), // Be careful to cast the entire slice to a raw pointer. ctrl: unsafe { NonNull::new_unchecked(Group::static_empty().as_ptr() as *mut u8) }, bucket_mask: 0, @@ -389,12 +377,11 @@ impl RawTable { fallability: Fallibility, ) -> Result { debug_assert!(buckets.is_power_of_two()); - let (layout, data_offset) = + let (layout, ctrl_offset) = calculate_layout::(buckets).ok_or_else(|| fallability.capacity_overflow())?; - let ctrl = NonNull::new(alloc(layout)).ok_or_else(|| fallability.alloc_err(layout))?; - let data = NonNull::new_unchecked(ctrl.as_ptr().add(data_offset) as *mut T); + let ptr = NonNull::new(alloc(layout)).ok_or_else(|| fallability.alloc_err(layout))?; + let ctrl = NonNull::new_unchecked(ptr.as_ptr().add(ctrl_offset)); Ok(Self { - data, ctrl, bucket_mask: buckets - 1, items: 0, @@ -433,15 +420,28 @@ impl RawTable { /// Deallocates the table without dropping any entries. #[cfg_attr(feature = "inline-more", inline)] unsafe fn free_buckets(&mut self) { - let (layout, _) = + let (layout, ctrl_offset) = calculate_layout::(self.buckets()).unwrap_or_else(|| hint::unreachable_unchecked()); - dealloc(self.ctrl.as_ptr(), layout); + dealloc(self.ctrl.as_ptr().sub(ctrl_offset), layout); + } + + + /// Returns pointer to one past last element of data table. + #[cfg_attr(feature = "inline-more", inline)] + pub unsafe fn data_backwards(&self) -> NonNull { + NonNull::new_unchecked(self.ctrl.as_ptr() as *mut T) + } + + /// Returns pointer to start of data table. + #[cfg_attr(feature = "inline-more", inline)] + pub unsafe fn compute_data_ptr(&self) -> NonNull { + NonNull::new_unchecked(self.data_backwards().as_ptr().sub(self.buckets())) } /// Returns the index of a bucket from a `Bucket`. #[cfg_attr(feature = "inline-more", inline)] pub unsafe fn bucket_index(&self, bucket: &Bucket) -> usize { - bucket.to_base_index(self.data) + bucket.to_base_index(self.data_backwards()) } /// Returns a pointer to a control byte. @@ -456,7 +456,7 @@ impl RawTable { pub unsafe fn bucket(&self, index: usize) -> Bucket { debug_assert_ne!(self.bucket_mask, 0); debug_assert!(index < self.buckets()); - Bucket::from_base_index(self.data, index) + Bucket::from_base_index(self.data_backwards(), index) } /// Erases an element from the table without dropping it. @@ -945,7 +945,7 @@ impl RawTable { /// struct, we have to make the `iter` method unsafe. #[cfg_attr(feature = "inline-more", inline)] pub unsafe fn iter(&self) -> RawIter { - let data = Bucket::from_base_index(self.data, 0); + let data = Bucket::from_base_index(self.data_backwards(), 0); RawIter { iter: RawIterRange::new(self.ctrl.as_ptr(), data, self.buckets()), items: self.items, @@ -973,9 +973,9 @@ impl RawTable { let alloc = if self.is_empty_singleton() { None } else { - let (layout, _) = calculate_layout::(self.buckets()) + let (layout, ctrl_offset) = calculate_layout::(self.buckets()) .unwrap_or_else(|| unsafe { hint::unreachable_unchecked() }); - Some((self.ctrl.cast(), layout)) + Some((unsafe { NonNull::new_unchecked(self.ctrl.as_ptr().sub(ctrl_offset)) }, layout)) }; mem::forget(self); alloc @@ -1060,9 +1060,8 @@ impl RawTableClone for RawTable { .ctrl(0) .copy_to_nonoverlapping(self.ctrl(0), self.num_ctrl_bytes()); source - .data - .as_ptr() - .copy_to_nonoverlapping(self.data.as_ptr(), self.buckets()); + .compute_data_ptr() + .copy_to_nonoverlapping(self.compute_data_ptr(), self.buckets()); self.items = source.items; self.growth_left = source.growth_left; @@ -1278,10 +1277,10 @@ impl RawIterRange { let tail = Self::new( self.next_ctrl.add(mid), - self.data.add(Group::WIDTH).add(mid), + self.data.next_n(Group::WIDTH).next_n(mid), len - mid, ); - debug_assert_eq!(self.data.add(Group::WIDTH).add(mid).ptr, tail.data.ptr); + debug_assert_eq!(self.data.next_n(Group::WIDTH).next_n(mid).ptr, tail.data.ptr); debug_assert_eq!(self.end, tail.end); self.end = self.next_ctrl.add(mid); debug_assert_eq!(self.end.add(Group::WIDTH), tail.next_ctrl); @@ -1317,7 +1316,7 @@ impl Iterator for RawIterRange { loop { if let Some(index) = self.current_group.lowest_set_bit() { self.current_group = self.current_group.remove_lowest_bit(); - return Some(self.data.add(index)); + return Some(self.data.next_n(index)); } if self.next_ctrl >= self.end { @@ -1330,7 +1329,7 @@ impl Iterator for RawIterRange { // EMPTY. On larger tables self.end is guaranteed to be aligned // to the group size (since tables are power-of-two sized). self.current_group = Group::load_aligned(self.next_ctrl).match_full(); - self.data = self.data.add(Group::WIDTH); + self.data = self.data.next_n(Group::WIDTH); self.next_ctrl = self.next_ctrl.add(Group::WIDTH); } } From 2fa183b11b07248911d6ab54d94185dfa5d48f91 Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Sun, 17 May 2020 21:45:17 +0300 Subject: [PATCH 02/11] change return type of compute_data_ptr --- src/raw/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 05000b201d..dc8c68519e 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -434,8 +434,8 @@ impl RawTable { /// Returns pointer to start of data table. #[cfg_attr(feature = "inline-more", inline)] - pub unsafe fn compute_data_ptr(&self) -> NonNull { - NonNull::new_unchecked(self.data_backwards().as_ptr().sub(self.buckets())) + pub unsafe fn compute_data_ptr(&self) -> *mut T { + self.data_backwards().as_ptr().sub(self.buckets()) } /// Returns the index of a bucket from a `Bucket`. From 850093a3403cff439fe14bd4e3242490c3979f6b Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Sun, 17 May 2020 22:24:59 +0300 Subject: [PATCH 03/11] add #[allow(dead_code)] to compute_data_ptr() fix build failure --- src/raw/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index dc8c68519e..c3f6b509af 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -434,6 +434,7 @@ impl RawTable { /// Returns pointer to start of data table. #[cfg_attr(feature = "inline-more", inline)] + #[allow(dead_code)] pub unsafe fn compute_data_ptr(&self) -> *mut T { self.data_backwards().as_ptr().sub(self.buckets()) } From 9a5fffc1e4a966643520fbca7ca1892b7af6c633 Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Mon, 18 May 2020 02:53:42 +0300 Subject: [PATCH 04/11] fix miri and clippy build errors doing `ptr as usize as *mut _` does escape from miri analysis ``` ptr: NonNull::new_unchecked(ptr), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a potentially NULL pointer, but expected something that cannot possibly fail to be greater or equal to 1 ``` I am not sure if this is correct way to fix it. --- src/raw/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index c3f6b509af..57039b2342 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -269,7 +269,7 @@ impl Bucket { } else { // the pointer arithmetic below might cross allocation bounds // because RawTable::iter() could call this function with empty table and index=0 - base.as_ptr().wrapping_offset(!index as isize) + base.as_ptr().wrapping_add(!index) as usize as *mut T }; Self { ptr: NonNull::new_unchecked(ptr), From 8002c2c75d631ad9b1d0a1a2c9ecd59d4da431d4 Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Wed, 20 May 2020 16:11:36 +0300 Subject: [PATCH 05/11] Update mod.rs - Removed unnecessary padding at the end of `calculate_layout` - Put back old comments - Renamed some functions - Use `#[cfg(feature = "...")]` instead `#[allow(dead_code)]` --- src/raw/mod.rs | 78 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 57039b2342..682d523eca 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -2,7 +2,6 @@ use crate::alloc::alloc::{alloc, dealloc, handle_alloc_error}; use crate::scopeguard::guard; use crate::CollectionAllocErr; use core::alloc::Layout; -use core::cmp; use core::hint; use core::iter::FusedIterator; use core::marker::PhantomData; @@ -209,34 +208,52 @@ fn bucket_mask_to_capacity(bucket_mask: usize) -> usize { } } +/// Returns a Layout which describes the allocation required for a hash table, +/// and the offset of the control bytes in the allocation. +/// (the offset is also one past last element of buckets) +/// +/// Returns `None` if an overflow occurs. #[cfg_attr(feature = "inline-more", inline)] -fn align_size(size: usize, align: usize) -> Option { - Some(size.checked_add(align - 1)? & !(align - 1)) +#[cfg(feature = "nightly")] +pub fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { + debug_assert!(buckets.is_power_of_two()); + + // Array of buckets + let padded_data = Layout::array::(buckets).ok()?.pad_to_align(); + + // Array of control bytes. This must be aligned to the group size. + // + // We add `Group::WIDTH` control bytes at the end of the array which + // replicate the bytes at the start of the array and thus avoids the need to + // perform bounds-checking while probing. + // + // There is no possible overflow here since buckets is a power of two and + // Group::WIDTH is a small number. + let ctrl = unsafe { Layout::from_size_align_unchecked(buckets + Group::WIDTH, Group::WIDTH) }; + + padded_data.extend(ctrl).ok() } -// Returns a Layout which describes the allocation required for a hash table, -// and the offset of the control bytes in the allocation. -// (the offset is also one past last element of data table) +/// Returns a Layout which describes the allocation required for a hash table, +/// and the offset of the control bytes in the allocation. +/// (the offset is also one past last element of buckets) /// /// Returns `None` if an overflow occurs. #[cfg_attr(feature = "inline-more", inline)] +#[cfg(not(feature = "nightly"))] fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { debug_assert!(buckets.is_power_of_two()); - let align = cmp::max(mem::align_of::(), Group::WIDTH); - - unsafe { - let data = Layout::from_size_align_unchecked( - align_size(mem::size_of::() * buckets, align)?, align); - - let ctrl = Layout::from_size_align_unchecked( - align_size(buckets + Group::WIDTH, align)?, align); + // Manual layout calculation since Layout methods are not yet stable. + let ctrl_align = usize::max(mem::align_of::(), Group::WIDTH); + let ctrl_offset = mem::size_of::().checked_mul(buckets)? + .checked_add(ctrl_align - 1)? & !(ctrl_align - 1); + let len = ctrl_offset.checked_add(buckets + Group::WIDTH)?; - let layout = Layout::from_size_align_unchecked( - data.size().checked_add(ctrl.size())?, align); - - Some((layout, /*ctrl offset*/ layout.size() - ctrl.size())) - } + Some(( + unsafe { Layout::from_size_align_unchecked(len, ctrl_align) }, + ctrl_offset + )) } /// A reference to a hash table bucket containing a `T`. @@ -336,8 +353,8 @@ pub struct RawTable { // number of buckets in the table. bucket_mask: usize, - // [Padding], .., T1, T2, ..., C1, C2, .., [Padding] - // ^ points here + // [Padding], T1, T2, ..., Tlast, C1, C2, ... + // ^ points here ctrl: NonNull, // Number of elements that can be inserted before we need to grow the table @@ -425,24 +442,23 @@ impl RawTable { dealloc(self.ctrl.as_ptr().sub(ctrl_offset), layout); } - /// Returns pointer to one past last element of data table. #[cfg_attr(feature = "inline-more", inline)] - pub unsafe fn data_backwards(&self) -> NonNull { + pub unsafe fn data_end(&self) -> NonNull { NonNull::new_unchecked(self.ctrl.as_ptr() as *mut T) } /// Returns pointer to start of data table. #[cfg_attr(feature = "inline-more", inline)] - #[allow(dead_code)] - pub unsafe fn compute_data_ptr(&self) -> *mut T { - self.data_backwards().as_ptr().sub(self.buckets()) + #[cfg(feature = "nightly")] + pub unsafe fn data_start(&self) -> *mut T { + self.data_end().as_ptr().sub(self.buckets()) } /// Returns the index of a bucket from a `Bucket`. #[cfg_attr(feature = "inline-more", inline)] pub unsafe fn bucket_index(&self, bucket: &Bucket) -> usize { - bucket.to_base_index(self.data_backwards()) + bucket.to_base_index(self.data_end()) } /// Returns a pointer to a control byte. @@ -457,7 +473,7 @@ impl RawTable { pub unsafe fn bucket(&self, index: usize) -> Bucket { debug_assert_ne!(self.bucket_mask, 0); debug_assert!(index < self.buckets()); - Bucket::from_base_index(self.data_backwards(), index) + Bucket::from_base_index(self.data_end(), index) } /// Erases an element from the table without dropping it. @@ -946,7 +962,7 @@ impl RawTable { /// struct, we have to make the `iter` method unsafe. #[cfg_attr(feature = "inline-more", inline)] pub unsafe fn iter(&self) -> RawIter { - let data = Bucket::from_base_index(self.data_backwards(), 0); + let data = Bucket::from_base_index(self.data_end(), 0); RawIter { iter: RawIterRange::new(self.ctrl.as_ptr(), data, self.buckets()), items: self.items, @@ -1061,8 +1077,8 @@ impl RawTableClone for RawTable { .ctrl(0) .copy_to_nonoverlapping(self.ctrl(0), self.num_ctrl_bytes()); source - .compute_data_ptr() - .copy_to_nonoverlapping(self.compute_data_ptr(), self.buckets()); + .data_start() + .copy_to_nonoverlapping(self.data_start(), self.buckets()); self.items = source.items; self.growth_left = source.growth_left; From a58cdf4fdea05fcb82227eba91da785d7db99d5a Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Thu, 21 May 2020 11:19:47 +0300 Subject: [PATCH 06/11] see description - nightly version of calculate_layout was putting padding between two tables in some cases, fixed. Also added debug_assert! to check this. - use wrapping_sub() instead sub() in data_start() (empty RawTable returns buckets() == 1, crosses allocation bounds) --- src/raw/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 682d523eca..0193debdf9 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -218,8 +218,9 @@ fn bucket_mask_to_capacity(bucket_mask: usize) -> usize { pub fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { debug_assert!(buckets.is_power_of_two()); + let common_align = usize::max(mem::align_of::(), Group::WIDTH); // Array of buckets - let padded_data = Layout::array::(buckets).ok()?.pad_to_align(); + let padded_data = Layout::array::(buckets).ok()?.align_to(common_align).ok()?.pad_to_align(); // Array of control bytes. This must be aligned to the group size. // @@ -230,6 +231,9 @@ pub fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { // There is no possible overflow here since buckets is a power of two and // Group::WIDTH is a small number. let ctrl = unsafe { Layout::from_size_align_unchecked(buckets + Group::WIDTH, Group::WIDTH) }; + + // There must be no padding between two tables. + debug_assert_eq!(padded_data.padding_needed_for(Group::WIDTH), 0); padded_data.extend(ctrl).ok() } @@ -452,7 +456,7 @@ impl RawTable { #[cfg_attr(feature = "inline-more", inline)] #[cfg(feature = "nightly")] pub unsafe fn data_start(&self) -> *mut T { - self.data_end().as_ptr().sub(self.buckets()) + self.data_end().as_ptr().wrapping_sub(self.buckets()) } /// Returns the index of a bucket from a `Bucket`. From 46328d84ad6fcdbc29b5b3c8247601575addccb6 Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Fri, 22 May 2020 11:40:10 +0300 Subject: [PATCH 07/11] remove wrapping_* pointer arithmetic --- src/raw/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 0193debdf9..0e2bafcce7 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -231,7 +231,7 @@ pub fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { // There is no possible overflow here since buckets is a power of two and // Group::WIDTH is a small number. let ctrl = unsafe { Layout::from_size_align_unchecked(buckets + Group::WIDTH, Group::WIDTH) }; - + // There must be no padding between two tables. debug_assert_eq!(padded_data.padding_needed_for(Group::WIDTH), 0); @@ -288,9 +288,7 @@ impl Bucket { // won't overflow because index must be less than length (index + 1) as *mut T } else { - // the pointer arithmetic below might cross allocation bounds - // because RawTable::iter() could call this function with empty table and index=0 - base.as_ptr().wrapping_add(!index) as usize as *mut T + base.as_ptr().sub(index) }; Self { ptr: NonNull::new_unchecked(ptr), @@ -301,8 +299,7 @@ impl Bucket { if mem::size_of::() == 0 { self.ptr.as_ptr() as usize - 1 } else { - //emulation of wrapping_offset_from - currently available only at nightly - (base.as_ptr() as usize - self.ptr.as_ptr() as usize) / mem::size_of::() - 1 + offset_from(base.as_ptr(), self.ptr.as_ptr()) } } #[cfg_attr(feature = "inline-more", inline)] @@ -311,7 +308,7 @@ impl Bucket { // Just return an arbitrary ZST pointer which is properly aligned mem::align_of::() as *mut T } else { - self.ptr.as_ptr() + self.ptr.as_ptr().sub(1) } } #[cfg_attr(feature = "inline-more", inline)] From 47c10e7eb719f343ca7a4e0eab3c50ac935fc49e Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Fri, 22 May 2020 13:10:57 +0300 Subject: [PATCH 08/11] remove pub from `fn calculate_layout` (it was inserted by mistake) --- src/raw/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 0e2bafcce7..9b7a9e5acc 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -215,7 +215,7 @@ fn bucket_mask_to_capacity(bucket_mask: usize) -> usize { /// Returns `None` if an overflow occurs. #[cfg_attr(feature = "inline-more", inline)] #[cfg(feature = "nightly")] -pub fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { +fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { debug_assert!(buckets.is_power_of_two()); let common_align = usize::max(mem::align_of::(), Group::WIDTH); From 7027781128a4896aea0aebd4f3f6590b8776dea9 Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Fri, 22 May 2020 20:29:44 +0300 Subject: [PATCH 09/11] Update mod.rs - Removed unnecessary code from nightly `calculate_layout` - Added comment for `Bucket::ptr` data member --- src/raw/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 9b7a9e5acc..0d7d2a56b6 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -218,9 +218,8 @@ fn bucket_mask_to_capacity(bucket_mask: usize) -> usize { fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { debug_assert!(buckets.is_power_of_two()); - let common_align = usize::max(mem::align_of::(), Group::WIDTH); // Array of buckets - let padded_data = Layout::array::(buckets).ok()?.align_to(common_align).ok()?.pad_to_align(); + let data = Layout::array::(buckets).ok()?; // Array of control bytes. This must be aligned to the group size. // @@ -235,7 +234,7 @@ fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { // There must be no padding between two tables. debug_assert_eq!(padded_data.padding_needed_for(Group::WIDTH), 0); - padded_data.extend(ctrl).ok() + data.extend(ctrl).ok() } /// Returns a Layout which describes the allocation required for a hash table, @@ -266,6 +265,9 @@ fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { /// is a ZST, then we instead track the index of the element in the table so /// that `erase` works properly. pub struct Bucket { + // Actually it is pointer to next element than element itself + // this is needed to maintain pointer arithmetic invariants + // keeping direct pointer to element introduces difficulty. // Using `NonNull` for variance and niche layout ptr: NonNull, } From 5a59dd32fa9579d0523b80961055390c5f4f300f Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Fri, 22 May 2020 20:47:57 +0300 Subject: [PATCH 10/11] fix old variable name --- src/raw/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 0d7d2a56b6..a683e6b2f8 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -232,7 +232,7 @@ fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { let ctrl = unsafe { Layout::from_size_align_unchecked(buckets + Group::WIDTH, Group::WIDTH) }; // There must be no padding between two tables. - debug_assert_eq!(padded_data.padding_needed_for(Group::WIDTH), 0); + debug_assert_eq!(data.padding_needed_for(Group::WIDTH), 0); data.extend(ctrl).ok() } From b500a0a8b72fbf5ae08c28285e4c20c650c1697b Mon Sep 17 00:00:00 2001 From: Iwa13 Date: Fri, 22 May 2020 21:28:54 +0300 Subject: [PATCH 11/11] fix test failure removed wrong assertation code --- src/raw/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index a683e6b2f8..fa7e4563b1 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -231,9 +231,6 @@ fn calculate_layout(buckets: usize) -> Option<(Layout, usize)> { // Group::WIDTH is a small number. let ctrl = unsafe { Layout::from_size_align_unchecked(buckets + Group::WIDTH, Group::WIDTH) }; - // There must be no padding between two tables. - debug_assert_eq!(data.padding_needed_for(Group::WIDTH), 0); - data.extend(ctrl).ok() }