88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11- use alloc:: { Global , Alloc , Layout , CollectionAllocErr , oom} ;
12- use cmp;
11+ use alloc:: { Global , Alloc , Layout , LayoutErr , CollectionAllocErr , oom} ;
1312use hash:: { BuildHasher , Hash , Hasher } ;
1413use marker;
15- use mem:: { align_of , size_of, needs_drop} ;
14+ use mem:: { size_of, needs_drop} ;
1615use mem;
1716use ops:: { Deref , DerefMut } ;
1817use ptr:: { self , Unique , NonNull } ;
@@ -651,64 +650,12 @@ impl<K, V, M> GapThenFull<K, V, M>
651650 }
652651}
653652
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)
712659}
713660
714661pub ( crate ) enum Fallibility {
@@ -735,37 +682,11 @@ impl<K, V> RawTable<K, V> {
735682 } ) ;
736683 }
737684
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-
743685 // Allocating hashmaps is a little tricky. We need to allocate two
744686 // arrays, but since we know their sizes and alignments up front,
745687 // we just allocate a single array, and then have the subarrays
746688 // 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) ?;
769690 let buffer = Global . alloc ( layout) . map_err ( |e| match fallibility {
770691 Infallible => oom ( layout) ,
771692 Fallible => e,
@@ -790,18 +711,12 @@ impl<K, V> RawTable<K, V> {
790711 }
791712
792713 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 ( ) ;
800715 let buffer = self . hashes . ptr ( ) as * mut u8 ;
801716 unsafe {
802717 RawBucket {
803718 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 ) ,
805720 idx : index,
806721 _marker : marker:: PhantomData ,
807722 }
@@ -1194,18 +1109,9 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
11941109 }
11951110 }
11961111
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 ( ) ;
12061113 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) ;
12091115 // Remember how everything was allocated out of one buffer
12101116 // during initialization? We only need one call to free here.
12111117 }
0 commit comments