@@ -67,17 +67,14 @@ use core::nonzero::NonZero;
67
67
pub struct RawTable < K , V > {
68
68
capacity : usize ,
69
69
size : usize ,
70
- hashes : Unique < Option < SafeHash > > ,
71
- // Because K/V do not appear directly in any of the types in the struct,
72
- // inform rustc that in fact instances of K and V are reachable from here.
73
- marker : marker:: PhantomData < ( K , V ) > ,
70
+ // NB. The table will probably need manual impls of Send and Sync if this
71
+ // field ever changes.
72
+ middle : Unique < ( K , V ) > ,
74
73
}
75
74
76
75
struct RawBucket < K , V > {
77
76
hash : * mut Option < SafeHash > ,
78
- key : * mut K ,
79
- val : * mut V ,
80
- _marker : marker:: PhantomData < ( K , V ) > ,
77
+ kval : * mut ( K , V ) ,
81
78
}
82
79
83
80
impl < K , V > Copy for RawBucket < K , V > { }
@@ -172,9 +169,7 @@ impl<K, V> RawBucket<K, V> {
172
169
unsafe fn offset ( self , count : isize ) -> RawBucket < K , V > {
173
170
RawBucket {
174
171
hash : self . hash . offset ( count) ,
175
- key : self . key . offset ( count) ,
176
- val : self . val . offset ( count) ,
177
- _marker : marker:: PhantomData ,
172
+ kval : self . kval . offset ( count) ,
178
173
}
179
174
}
180
175
}
@@ -202,6 +197,7 @@ impl<K, V> Put for RawTable<K, V> {}
202
197
impl < ' t , K , V > Put for & ' t mut RawTable < K , V > { }
203
198
impl < K , V , M : Put > Put for Bucket < K , V , M > { }
204
199
impl < K , V , M : Put > Put for FullBucket < K , V , M > { }
200
+
205
201
// Buckets hold references to the table.
206
202
impl < K , V , M , S > Bucket < K , V , M , S > {
207
203
/// Borrow a reference to the table.
@@ -245,7 +241,6 @@ impl<K, V, M> Bucket<K, V, M> where M: Borrow<RawTable<K, V>> {
245
241
capacity : capacity,
246
242
table : table,
247
243
} ;
248
-
249
244
if capacity == 0 {
250
245
Err ( bucket. unsafe_cast ( ) )
251
246
} else {
@@ -350,8 +345,7 @@ impl<K, V, M> EmptyBucket<K, V, M> where M: Borrow<RawTable<K, V>>, M: Put {
350
345
-> FullBucket < K , V , M > {
351
346
unsafe {
352
347
* self . raw . hash = Some ( hash) ;
353
- ptr:: write ( self . 0 . raw . key , key) ;
354
- ptr:: write ( self . 0 . raw . val , value) ;
348
+ ptr:: write ( self . raw . kval , ( key, value) ) ;
355
349
}
356
350
357
351
self . table . size += 1 ;
@@ -375,56 +369,49 @@ impl<'t, K, V, M: 't> FullBucket<K, V, M> where M: Borrow<RawTable<K, V>> {
375
369
376
370
/// Gets references to the key and value at a given index.
377
371
pub fn read ( & self ) -> ( & SafeHash , & K , & V ) {
378
- unsafe {
379
- ( & * ( self . 0 . raw . hash as * mut SafeHash ) ,
380
- & * self . 0 . raw . key ,
381
- & * self . 0 . raw . val )
382
- }
383
- }
384
- }
385
-
386
- impl < K , V , M > FullBucket < K , V , M > where M : Borrow < RawTable < K , V > > {
387
- /// Removes this bucket's key and value from the hashtable.
388
- ///
389
- /// This works similarly to `put`, building an `EmptyBucket` out of the
390
- /// taken bucket.
391
- pub fn take ( mut self ) -> ( EmptyBucket < K , V , M > , K , V ) {
392
- self . 0 . table . size -= 1 ;
393
-
394
- unsafe {
395
- * self . 0 . raw . hash = None ;
396
- let k = ptr:: read ( self . 0 . raw . key ) ;
397
- let v = ptr:: read ( self . 0 . raw . val ) ;
398
- ( Bucket ( self . 0 ) , k, v)
399
- }
400
- }
401
-
402
- /// Gets mutable references to the key and value at a given index.
403
- pub fn read_mut ( & mut self ) -> ( & mut SafeHash , & mut K , & mut V ) {
404
- unsafe {
405
- ( & mut * ( self . 0 . raw . hash as * mut SafeHash ) ,
406
- & mut * self . 0 . raw . key ,
407
- & mut * self . 0 . raw . val )
408
- }
372
+ let ( & ref h, & ( ref k, ref v) ) = unsafe {
373
+ ( & * ( self . raw . hash as * mut SafeHash ) , & * self . raw . kval )
374
+ } ;
375
+ ( h, k, v)
409
376
}
410
- }
411
377
412
- impl < ' t , K , V , M : ' t > FullBucket < K , V , M > where M : Borrow < RawTable < K , V > > {
413
378
/// Exchange a bucket state for immutable references into the table.
414
379
/// Because the underlying reference to the table is also consumed,
415
380
/// no further changes to the structure of the table are possible;
416
381
/// in exchange for this, the returned references have a longer lifetime
417
382
/// than the references returned by `read()`.
418
383
pub fn into_refs ( self ) -> ( & ' t K , & ' t V ) {
419
- self . 0 . raw . into_refs ( )
384
+ unsafe { ( & ( * self . raw . kval ) . 0 , & ( * self . raw . kval ) . 1 ) }
420
385
}
421
386
}
422
387
423
- impl < ' t , K , V , M : ' t > FullBucket < K , V , M > where M : Borrow < RawTable < K , V > > {
388
+ impl < ' t , K , V , M : ' t > FullBucket < K , V , M > where M : BorrowMut < RawTable < K , V > > {
389
+ /// Gets mutable references to the key and value at a given index.
390
+ pub fn read_mut ( & mut self ) -> ( & mut SafeHash , & mut K , & mut V ) {
391
+ let ( & mut ref mut h, & mut ( ref mut k, ref mut v) ) = unsafe {
392
+ ( & mut * ( self . raw . hash as * mut SafeHash ) , & mut * self . raw . kval )
393
+ } ;
394
+ ( h, k, v)
395
+ }
396
+
424
397
/// This works similarly to `into_refs`, exchanging a bucket state
425
398
/// for mutable references into the table.
426
399
pub fn into_mut_refs ( self ) -> ( & ' t mut K , & ' t mut V ) {
427
- self . 0 . raw . into_mut_refs ( )
400
+ unsafe { ( & mut ( * self . raw . kval ) . 0 , & mut ( * self . raw . kval ) . 1 ) }
401
+ }
402
+
403
+ /// Removes this bucket's key and value from the hashtable.
404
+ ///
405
+ /// This works similarly to `put`, building an `EmptyBucket` out of the
406
+ /// taken bucket.
407
+ pub fn take ( mut self ) -> ( EmptyBucket < K , V , M > , K , V ) {
408
+ self . table . size -= 1 ;
409
+
410
+ unsafe {
411
+ * self . raw . hash = None ;
412
+ let ( k, v) = ptr:: read ( self . raw . kval ) ;
413
+ ( self . unsafe_cast ( ) , k, v)
414
+ }
428
415
}
429
416
}
430
417
@@ -437,8 +424,7 @@ impl<K, V, M> GapThenFull<K, V, M> where M: Borrow<RawTable<K, V>> {
437
424
pub fn shift ( mut self ) -> Option < GapThenFull < K , V , M > > {
438
425
unsafe {
439
426
* self . gap . raw . hash = mem:: replace ( & mut * self . full . raw . hash , None ) ;
440
- ptr:: copy_nonoverlapping ( self . gap . 0 . raw . key , self . full . 0 . raw . key , 1 ) ;
441
- ptr:: copy_nonoverlapping ( self . gap . 0 . raw . val , self . full . 0 . raw . val , 1 ) ;
427
+ ptr:: copy_nonoverlapping ( self . gap . raw . kval , self . full . raw . kval , 1 ) ;
442
428
}
443
429
444
430
let Bucket { raw : prev_raw, idx : prev_idx, .. } = self . full ;
@@ -457,117 +443,34 @@ impl<K, V, M> GapThenFull<K, V, M> where M: Borrow<RawTable<K, V>> {
457
443
}
458
444
}
459
445
460
- /// Rounds up to a multiple of a power of two. Returns the closest multiple
461
- /// of `target_alignment` that is higher or equal to `unrounded`.
462
- ///
463
- /// # Panics
464
- ///
465
- /// Panics if `target_alignment` is not a power of two.
466
- fn round_up_to_next ( unrounded : usize , target_alignment : usize ) -> usize {
467
- assert ! ( target_alignment. is_power_of_two( ) ) ;
468
- ( unrounded + target_alignment - 1 ) & !( target_alignment - 1 )
469
- }
470
-
471
- #[ test]
472
- fn test_rounding ( ) {
473
- assert_eq ! ( round_up_to_next( 0 , 4 ) , 0 ) ;
474
- assert_eq ! ( round_up_to_next( 1 , 4 ) , 4 ) ;
475
- assert_eq ! ( round_up_to_next( 2 , 4 ) , 4 ) ;
476
- assert_eq ! ( round_up_to_next( 3 , 4 ) , 4 ) ;
477
- assert_eq ! ( round_up_to_next( 4 , 4 ) , 4 ) ;
478
- assert_eq ! ( round_up_to_next( 5 , 4 ) , 8 ) ;
479
- }
480
-
481
- // Returns a tuple of (key_offset, val_offset),
482
- // from the start of a mallocated array.
483
- fn calculate_offsets ( hashes_size : usize ,
484
- keys_size : usize , keys_align : usize ,
485
- vals_align : usize )
486
- -> ( usize , usize ) {
487
- let keys_offset = round_up_to_next ( hashes_size, keys_align) ;
488
- let end_of_keys = keys_offset + keys_size;
489
-
490
- let vals_offset = round_up_to_next ( end_of_keys, vals_align) ;
491
-
492
- ( keys_offset, vals_offset)
493
- }
494
-
495
- // Returns a tuple of (minimum required malloc alignment, hash_offset,
496
- // array_size), from the start of a mallocated array.
497
- fn calculate_allocation ( hash_size : usize , hash_align : usize ,
498
- keys_size : usize , keys_align : usize ,
499
- vals_size : usize , vals_align : usize )
500
- -> ( usize , usize , usize ) {
501
- let hash_offset = 0 ;
502
- let ( _, vals_offset) = calculate_offsets ( hash_size,
503
- keys_size, keys_align,
504
- vals_align) ;
505
- let end_of_vals = vals_offset + vals_size;
506
-
507
- let min_align = cmp:: max ( hash_align, cmp:: max ( keys_align, vals_align) ) ;
508
-
509
- ( min_align, hash_offset, end_of_vals)
510
- }
511
-
512
- #[ test]
513
- fn test_offset_calculation ( ) {
514
- assert_eq ! ( calculate_allocation( 128 , 8 , 15 , 1 , 4 , 4 ) , ( 8 , 0 , 148 ) ) ;
515
- assert_eq ! ( calculate_allocation( 3 , 1 , 2 , 1 , 1 , 1 ) , ( 1 , 0 , 6 ) ) ;
516
- assert_eq ! ( calculate_allocation( 6 , 2 , 12 , 4 , 24 , 8 ) , ( 8 , 0 , 48 ) ) ;
517
- assert_eq ! ( calculate_offsets( 128 , 15 , 1 , 4 ) , ( 128 , 144 ) ) ;
518
- assert_eq ! ( calculate_offsets( 3 , 2 , 1 , 1 ) , ( 3 , 5 ) ) ;
519
- assert_eq ! ( calculate_offsets( 6 , 12 , 4 , 8 ) , ( 8 , 24 ) ) ;
520
- }
521
-
522
446
impl < K , V > RawTable < K , V > {
523
447
/// Does not initialize the buckets.
524
- unsafe fn new_uninitialized ( capacity : uint ) -> RawTable < K , V > {
525
- if capacity == 0 {
526
- return RawTable {
527
- size : 0 ,
528
- capacity : 0 ,
529
- hashes : Unique :: new ( EMPTY as * mut u64 ) ,
530
- marker : marker:: PhantomData ,
448
+ pub fn new_uninitialized ( capacity : uint ) -> PartialRawTable < K , V > {
449
+ unsafe {
450
+ let table = if capacity == 0 {
451
+ RawTable {
452
+ size : 0 ,
453
+ capacity : 0 ,
454
+ middle : Unique :: new ( EMPTY as * mut _ ) ,
455
+ }
456
+ } else {
457
+ let hashes = allocate ( checked_size_generic :: < K , V > ( capacity) , align :: < K , V > ( ) ) ;
458
+ if hashes. is_null ( ) { :: alloc:: oom ( ) }
459
+
460
+ RawTable {
461
+ capacity : capacity,
462
+ size : 0 ,
463
+ middle : Unique ( ( hashes as * mut ( K , V ) ) . offset ( capacity as isize ) ) ,
464
+ }
531
465
} ;
532
- }
533
466
534
- // No need for `checked_mul` before a more restrictive check performed
535
- // later in this method.
536
- let hashes_size = capacity * size_of :: < Option < SafeHash > > ( ) ;
537
- let keys_size = capacity * size_of :: < K > ( ) ;
538
- let vals_size = capacity * size_of :: < V > ( ) ;
539
-
540
- // Allocating hashmaps is a little tricky. We need to allocate three
541
- // arrays, but since we know their sizes and alignments up front,
542
- // we just allocate a single array, and then have the subarrays
543
- // point into it.
544
- //
545
- // This is great in theory, but in practice getting the alignment
546
- // right is a little subtle. Therefore, calculating offsets has been
547
- // factored out into a different function.
548
- let ( malloc_alignment, hash_offset, size) =
549
- calculate_allocation (
550
- hashes_size, min_align_of :: < Option < SafeHash > > ( ) ,
551
- keys_size, min_align_of :: < K > ( ) ,
552
- vals_size, min_align_of :: < V > ( ) ) ;
553
-
554
- // One check for overflow that covers calculation and rounding of size.
555
- let size_of_bucket = size_of :: < Option < SafeHash > > ( ) . checked_add ( size_of :: < K > ( ) ) . unwrap ( )
556
- . checked_add ( size_of :: < V > ( ) ) . unwrap ( ) ;
557
- assert ! ( size >= capacity. checked_mul( size_of_bucket)
558
- . expect( "capacity overflow" ) ,
559
- "capacity overflow" ) ;
560
-
561
- let buffer = allocate ( size, malloc_alignment) ;
562
- if buffer. is_null ( ) { :: alloc:: oom ( ) }
563
-
564
- let hashes = buffer. offset ( hash_offset as isize ) as * mut Option < SafeHash > ;
565
-
566
- RawTable {
567
- capacity : capacity,
568
- size : 0 ,
569
- hashes : Unique :: new ( hashes) ,
570
- marker : marker:: PhantomData ,
467
+ PartialRawTable {
468
+ front : table. first_bucket_raw ( ) ,
469
+ back : table. first_bucket_raw ( ) ,
470
+ front_num : 0 ,
471
+ back_num : capacity,
472
+ table : table,
473
+ }
571
474
}
572
475
}
573
476
@@ -582,23 +485,12 @@ impl<K, V> RawTable<K, V> {
582
485
if self . capacity ( ) == 0 {
583
486
RawBucket {
584
487
hash : ptr:: null_mut ( ) ,
585
- key : ptr:: null_mut ( ) ,
586
- val : ptr:: null_mut ( ) ,
488
+ kval : ptr:: null_mut ( ) ,
587
489
}
588
490
} else {
589
- let hashes_size = self . capacity * size_of :: < Option < SafeHash > > ( ) ;
590
- let keys_size = self . capacity * size_of :: < K > ( ) ;
591
-
592
- let buffer = * self . hashes as * mut u8 ;
593
- let ( keys_offset, vals_offset) = calculate_offsets ( hashes_size,
594
- keys_size, min_align_of :: < K > ( ) ,
595
- min_align_of :: < V > ( ) ) ;
596
-
597
491
RawBucket {
598
- hash : * self . middle as * mut Option < SafeHash > ,
599
- key : buffer. offset ( keys_offset as isize ) as * mut K ,
600
- val : buffer. offset ( vals_offset as isize ) as * mut V ,
601
- _marker : marker:: PhantomData ,
492
+ hash : self . middle . ptr as * mut Option < SafeHash > ,
493
+ kval : self . middle . ptr . offset ( -( self . capacity as isize ) ) ,
602
494
}
603
495
}
604
496
}
@@ -613,14 +505,61 @@ impl<K, V> RawTable<K, V> {
613
505
pub fn size ( & self ) -> usize {
614
506
self . size
615
507
}
508
+ }
616
509
510
+ /// Rounds up to a multiple of a power of two. Returns the closest multiple
511
+ /// of `target_alignment` that is higher or equal to `unrounded`.
512
+ ///
513
+ /// # Panics
514
+ ///
515
+ /// Panics if `target_alignment` is not a power of two.
516
+ fn round_up_to_next ( unrounded : uint , target_alignment : uint ) -> uint {
517
+ assert ! ( target_alignment. is_power_of_two( ) ) ;
518
+ ( unrounded + target_alignment - 1 ) & !( target_alignment - 1 )
617
519
}
618
520
521
+ #[ test]
522
+ fn test_rounding ( ) {
523
+ assert_eq ! ( round_up_to_next( 0 , 4 ) , 0 ) ;
524
+ assert_eq ! ( round_up_to_next( 1 , 4 ) , 4 ) ;
525
+ assert_eq ! ( round_up_to_next( 2 , 4 ) , 4 ) ;
526
+ assert_eq ! ( round_up_to_next( 3 , 4 ) , 4 ) ;
527
+ assert_eq ! ( round_up_to_next( 4 , 4 ) , 4 ) ;
528
+ assert_eq ! ( round_up_to_next( 5 , 4 ) , 8 ) ;
529
+ assert_eq ! ( round_up_to_next( 5 , 8 ) , 8 ) ;
530
+ }
619
531
532
+ #[ inline]
533
+ fn size_generic < K , V > ( capacity : usize ) -> usize {
534
+ let hash_align = min_align_of :: < Option < SafeHash > > ( ) ;
535
+ round_up_to_next ( size_of :: < ( K , V ) > ( ) * capacity, hash_align) + size_of :: < SafeHash > ( ) * capacity
536
+ }
620
537
538
+ fn checked_size_generic < K , V > ( capacity : usize ) -> usize {
539
+ let size = size_generic :: < K , V > ( capacity) ;
540
+ let elem_size = size_of :: < ( K , V ) > ( ) + size_of :: < SafeHash > ( ) ;
541
+ assert ! ( size >= capacity. checked_mul( elem_size) . expect( "capacity overflow" ) ,
542
+ "capacity overflow" ) ;
543
+ size
544
+ }
545
+
546
+ #[ inline]
547
+ fn align < K , V > ( ) -> usize {
548
+ cmp:: max ( mem:: min_align_of :: < ( K , V ) > ( ) , mem:: min_align_of :: < u64 > ( ) )
549
+ }
550
+
551
+ /// A newtyped RawBucket. Not copyable.
552
+ pub struct RawFullBucket < K , V , M > ( RawBucket < K , V > ) ;
553
+
554
+ impl < ' t , K , V , M : ' t > RawFullBucket < K , V , M > where RawTable < K , V > : BorrowFrom < M > {
555
+ pub fn into_refs ( self ) -> ( & ' t K , & ' t V ) {
556
+ unsafe { ( & ( * self . 0 . kval ) . 0 , & ( * self . 0 . kval ) . 1 ) }
621
557
}
622
558
}
623
559
560
+ impl < ' t , K , V , M : ' t > RawFullBucket < K , V , M > where RawTable < K , V > : BorrowFromMut < M > {
561
+ pub fn into_mut_refs ( self ) -> ( & ' t mut K , & ' t mut V ) {
562
+ unsafe { ( & mut ( * self . 0 . kval ) . 0 , & mut ( * self . 0 . kval ) . 1 ) }
624
563
}
625
564
}
626
565
0 commit comments