8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use alloc:: { Global , Alloc , Layout , CollectionAllocErr , oom} ;
12
- use cmp;
11
+ use alloc:: { Global , Alloc , Layout , LayoutErr , CollectionAllocErr , oom} ;
13
12
use hash:: { BuildHasher , Hash , Hasher } ;
14
13
use marker;
15
- use mem:: { align_of , size_of, needs_drop} ;
14
+ use mem:: { size_of, needs_drop} ;
16
15
use mem;
17
16
use ops:: { Deref , DerefMut } ;
18
17
use ptr:: { self , Unique , NonNull } ;
@@ -651,64 +650,12 @@ impl<K, V, M> GapThenFull<K, V, M>
651
650
}
652
651
}
653
652
654
-
655
- /// Rounds up to a multiple of a power of two. Returns the closest multiple
656
- /// of `target_alignment` that is higher or equal to `unrounded`.
657
- ///
658
- /// # Panics
659
- ///
660
- /// Panics if `target_alignment` is not a power of two.
661
- #[ inline]
662
- fn round_up_to_next ( unrounded : usize , target_alignment : usize ) -> usize {
663
- assert ! ( target_alignment. is_power_of_two( ) ) ;
664
- ( unrounded + target_alignment - 1 ) & !( target_alignment - 1 )
665
- }
666
-
667
- #[ test]
668
- fn test_rounding ( ) {
669
- assert_eq ! ( round_up_to_next( 0 , 4 ) , 0 ) ;
670
- assert_eq ! ( round_up_to_next( 1 , 4 ) , 4 ) ;
671
- assert_eq ! ( round_up_to_next( 2 , 4 ) , 4 ) ;
672
- assert_eq ! ( round_up_to_next( 3 , 4 ) , 4 ) ;
673
- assert_eq ! ( round_up_to_next( 4 , 4 ) , 4 ) ;
674
- assert_eq ! ( round_up_to_next( 5 , 4 ) , 8 ) ;
675
- }
676
-
677
- // Returns a tuple of (pairs_offset, end_of_pairs_offset),
678
- // from the start of a mallocated array.
679
- #[ inline]
680
- fn calculate_offsets ( hashes_size : usize ,
681
- pairs_size : usize ,
682
- pairs_align : usize )
683
- -> ( usize , usize , bool ) {
684
- let pairs_offset = round_up_to_next ( hashes_size, pairs_align) ;
685
- let ( end_of_pairs, oflo) = pairs_offset. overflowing_add ( pairs_size) ;
686
-
687
- ( pairs_offset, end_of_pairs, oflo)
688
- }
689
-
690
- // Returns a tuple of (minimum required malloc alignment,
691
- // array_size), from the start of a mallocated array.
692
- fn calculate_allocation ( hash_size : usize ,
693
- hash_align : usize ,
694
- pairs_size : usize ,
695
- pairs_align : usize )
696
- -> ( usize , usize , bool ) {
697
- let ( _, end_of_pairs, oflo) = calculate_offsets ( hash_size, pairs_size, pairs_align) ;
698
-
699
- let align = cmp:: max ( hash_align, pairs_align) ;
700
-
701
- ( align, end_of_pairs, oflo)
702
- }
703
-
704
- #[ test]
705
- fn test_offset_calculation ( ) {
706
- assert_eq ! ( calculate_allocation( 128 , 8 , 16 , 8 ) , ( 8 , 144 , false ) ) ;
707
- assert_eq ! ( calculate_allocation( 3 , 1 , 2 , 1 ) , ( 1 , 5 , false ) ) ;
708
- assert_eq ! ( calculate_allocation( 6 , 2 , 12 , 4 ) , ( 4 , 20 , false ) ) ;
709
- assert_eq ! ( calculate_offsets( 128 , 15 , 4 ) , ( 128 , 143 , false ) ) ;
710
- assert_eq ! ( calculate_offsets( 3 , 2 , 4 ) , ( 4 , 6 , false ) ) ;
711
- assert_eq ! ( calculate_offsets( 6 , 12 , 4 ) , ( 8 , 20 , false ) ) ;
653
+ // Returns a Layout which describes the allocation required for a hash table,
654
+ // and the offset of the array of (key, value) pairs in the allocation.
655
+ fn calculate_layout < K , V > ( capacity : usize ) -> Result < ( Layout , usize ) , LayoutErr > {
656
+ let hashes = Layout :: array :: < HashUint > ( capacity) ?;
657
+ let pairs = Layout :: array :: < ( K , V ) > ( capacity) ?;
658
+ hashes. extend ( pairs)
712
659
}
713
660
714
661
pub ( crate ) enum Fallibility {
@@ -735,37 +682,11 @@ impl<K, V> RawTable<K, V> {
735
682
} ) ;
736
683
}
737
684
738
- // No need for `checked_mul` before a more restrictive check performed
739
- // later in this method.
740
- let hashes_size = capacity. wrapping_mul ( size_of :: < HashUint > ( ) ) ;
741
- let pairs_size = capacity. wrapping_mul ( size_of :: < ( K , V ) > ( ) ) ;
742
-
743
685
// Allocating hashmaps is a little tricky. We need to allocate two
744
686
// arrays, but since we know their sizes and alignments up front,
745
687
// we just allocate a single array, and then have the subarrays
746
688
// point into it.
747
- //
748
- // This is great in theory, but in practice getting the alignment
749
- // right is a little subtle. Therefore, calculating offsets has been
750
- // factored out into a different function.
751
- let ( alignment, size, oflo) = calculate_allocation ( hashes_size,
752
- align_of :: < HashUint > ( ) ,
753
- pairs_size,
754
- align_of :: < ( K , V ) > ( ) ) ;
755
- if oflo {
756
- return Err ( CollectionAllocErr :: CapacityOverflow ) ;
757
- }
758
-
759
- // One check for overflow that covers calculation and rounding of size.
760
- let size_of_bucket = size_of :: < HashUint > ( ) . checked_add ( size_of :: < ( K , V ) > ( ) )
761
- . ok_or ( CollectionAllocErr :: CapacityOverflow ) ?;
762
- let capacity_mul_size_of_bucket = capacity. checked_mul ( size_of_bucket) ;
763
- if capacity_mul_size_of_bucket. is_none ( ) || size < capacity_mul_size_of_bucket. unwrap ( ) {
764
- return Err ( CollectionAllocErr :: CapacityOverflow ) ;
765
- }
766
-
767
- let layout = Layout :: from_size_align ( size, alignment)
768
- . map_err ( |_| CollectionAllocErr :: CapacityOverflow ) ?;
689
+ let ( layout, _) = calculate_layout :: < K , V > ( capacity) ?;
769
690
let buffer = Global . alloc ( layout) . map_err ( |e| match fallibility {
770
691
Infallible => oom ( layout) ,
771
692
Fallible => e,
@@ -790,18 +711,12 @@ impl<K, V> RawTable<K, V> {
790
711
}
791
712
792
713
fn raw_bucket_at ( & self , index : usize ) -> RawBucket < K , V > {
793
- let hashes_size = self . capacity ( ) * size_of :: < HashUint > ( ) ;
794
- let pairs_size = self . capacity ( ) * size_of :: < ( K , V ) > ( ) ;
795
-
796
- let ( pairs_offset, _, oflo) =
797
- calculate_offsets ( hashes_size, pairs_size, align_of :: < ( K , V ) > ( ) ) ;
798
- debug_assert ! ( !oflo, "capacity overflow" ) ;
799
-
714
+ let ( _, pairs_offset) = calculate_layout :: < K , V > ( self . capacity ( ) ) . unwrap ( ) ;
800
715
let buffer = self . hashes . ptr ( ) as * mut u8 ;
801
716
unsafe {
802
717
RawBucket {
803
718
hash_start : buffer as * mut HashUint ,
804
- pair_start : buffer. offset ( pairs_offset as isize ) as * const ( K , V ) ,
719
+ pair_start : buffer. add ( pairs_offset) as * const ( K , V ) ,
805
720
idx : index,
806
721
_marker : marker:: PhantomData ,
807
722
}
@@ -1194,18 +1109,9 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
1194
1109
}
1195
1110
}
1196
1111
1197
- let hashes_size = self . capacity ( ) * size_of :: < HashUint > ( ) ;
1198
- let pairs_size = self . capacity ( ) * size_of :: < ( K , V ) > ( ) ;
1199
- let ( align, size, oflo) = calculate_allocation ( hashes_size,
1200
- align_of :: < HashUint > ( ) ,
1201
- pairs_size,
1202
- align_of :: < ( K , V ) > ( ) ) ;
1203
-
1204
- debug_assert ! ( !oflo, "should be impossible" ) ;
1205
-
1112
+ let ( layout, _) = calculate_layout :: < K , V > ( self . capacity ( ) ) . unwrap ( ) ;
1206
1113
unsafe {
1207
- Global . dealloc ( NonNull :: new_unchecked ( self . hashes . ptr ( ) ) . as_opaque ( ) ,
1208
- Layout :: from_size_align ( size, align) . unwrap ( ) ) ;
1114
+ Global . dealloc ( NonNull :: new_unchecked ( self . hashes . ptr ( ) ) . as_opaque ( ) , layout) ;
1209
1115
// Remember how everything was allocated out of one buffer
1210
1116
// during initialization? We only need one call to free here.
1211
1117
}
0 commit comments