@@ -33,16 +33,17 @@ mod table {
33
33
extern crate libc;
34
34
35
35
use std:: clone:: Clone ;
36
+ use std:: cmp;
36
37
use std:: cmp:: Eq ;
37
38
use std:: hash:: { Hash , Hasher } ;
38
39
use std:: kinds:: marker;
39
- use std:: num:: CheckedMul ;
40
+ use std:: num:: { CheckedMul , is_power_of_two } ;
40
41
use std:: option:: { Option , Some , None } ;
41
42
use std:: prelude:: Drop ;
42
43
use std:: ptr;
43
44
use std:: ptr:: RawPtr ;
44
45
use std:: rt:: global_heap;
45
- use std:: intrinsics:: { size_of, transmute, move_val_init} ;
46
+ use std:: intrinsics:: { size_of, min_align_of , transmute, move_val_init} ;
46
47
use std:: iter:: { Iterator , range_step_inclusive} ;
47
48
48
49
static EMPTY_BUCKET : u64 = 0u64 ;
@@ -163,6 +164,42 @@ mod table {
163
164
}
164
165
}
165
166
167
+ fn round_up_to_next ( unrounded : uint , target_alignment : uint ) -> uint {
168
+ assert ! ( is_power_of_two( target_alignment) ) ;
169
+ ( unrounded + target_alignment - 1 ) & !( target_alignment - 1 )
170
+ }
171
+
172
+ #[ test]
173
+ fn test_rounding ( ) {
174
+ assert ! ( round_up_to_next( 0 , 4 ) == 0 ) ;
175
+ assert ! ( round_up_to_next( 1 , 4 ) == 4 ) ;
176
+ assert ! ( round_up_to_next( 2 , 4 ) == 4 ) ;
177
+ assert ! ( round_up_to_next( 3 , 4 ) == 4 ) ;
178
+ assert ! ( round_up_to_next( 4 , 4 ) == 4 ) ;
179
+ assert ! ( round_up_to_next( 5 , 4 ) == 8 ) ;
180
+ }
181
+
182
+ // Returns a tuple of (minimum required malloc alignment, hash_offset,
183
+ // key_offset, val_offset, array_size), from the start of a mallocated array.
184
+ fn calculate_offsets (
185
+ hash_size : uint , hash_align : uint ,
186
+ keys_size : uint , keys_align : uint ,
187
+ vals_size : uint , vals_align : uint ) -> ( uint , uint , uint , uint , uint ) {
188
+
189
+ let hash_offset = 0 ;
190
+ let end_of_hashes = hash_offset + hash_size;
191
+
192
+ let keys_offset = round_up_to_next ( end_of_hashes, keys_align) ;
193
+ let end_of_keys = keys_offset + keys_size;
194
+
195
+ let vals_offset = round_up_to_next ( end_of_keys, vals_align) ;
196
+ let end_of_vals = vals_offset + vals_size;
197
+
198
+ let min_align = cmp:: max ( hash_align, cmp:: max ( keys_align, vals_align) ) ;
199
+
200
+ ( min_align, hash_offset, keys_offset, vals_offset, end_of_vals)
201
+ }
202
+
166
203
impl < K , V > RawTable < K , V > {
167
204
168
205
/// Does not initialize the buckets. The caller should ensure they,
@@ -175,32 +212,31 @@ mod table {
175
212
let vals_size =
176
213
capacity. checked_mul ( & size_of :: < V > ( ) ) . expect ( "capacity overflow" ) ;
177
214
178
- /*
179
- The following code was my first pass at making RawTable only
180
- allocate a single buffer, since that's all it needs. There's
181
- no logical reason for this to require three calls to malloc.
182
-
183
- However, I'm not convinced the code below is correct. If you
184
- want to take a stab at it, please do! The alignment is
185
- especially tricky to get right, especially if you need more
186
- alignment than malloc guarantees.
187
-
188
- let hashes_offset = 0;
189
- let keys_offset = align_size(hashes_offset + hashes_size, keys_align);
190
- let vals_offset = align_size(keys_offset + keys_size, vals_align);
191
- let end = vals_offset + vals_size;
192
-
193
- let buffer = global_heap::malloc_raw(end);
194
-
195
- let hashes = buffer.offset(hashes_offset) as *mut u64;
196
- let keys = buffer.offset(keys_offset) as *mut K;
197
- let vals = buffer.offset(vals_offset) as *mut V;
198
-
199
- */
200
-
201
- let hashes = global_heap:: malloc_raw ( hashes_size) as * mut u64 ;
202
- let keys = global_heap:: malloc_raw ( keys_size) as * mut K ;
203
- let vals = global_heap:: malloc_raw ( vals_size) as * mut V ;
215
+ // Allocating hashmaps is a little tricky. We need to allocate three
216
+ // arrays here, but since we know their sizes and alignments up front,
217
+ // we could theoretically allocate only a single array, and then have
218
+ // the subarrays just point into it.
219
+ //
220
+ // This is great in theory, but in practice getting the alignment
221
+ // right is a little subtle. Therefore, calculating offsets has been
222
+ // factored out into a different function.
223
+ let ( malloc_alignment, hash_offset, keys_offset, vals_offset, size) =
224
+ calculate_offsets (
225
+ hashes_size, min_align_of :: < u64 > ( ) ,
226
+ keys_size, min_align_of :: < K > ( ) ,
227
+ vals_size, min_align_of :: < V > ( ) ) ;
228
+
229
+ let buffer = global_heap:: malloc_raw ( size) as * mut u8 ;
230
+
231
+ // FIXME #13094: If malloc was not at as aligned as we expected,
232
+ // our offset calculations are just plain wrong. We could support
233
+ // any alignment if we switched from `malloc` to `posix_memalign`.
234
+ assert ! ( round_up_to_next( buffer as uint, malloc_alignment)
235
+ == ( buffer as uint) ) ;
236
+
237
+ let hashes = buffer. offset ( hash_offset as int ) as * mut u64 ;
238
+ let keys = buffer. offset ( keys_offset as int ) as * mut K ;
239
+ let vals = buffer. offset ( vals_offset as int ) as * mut V ;
204
240
205
241
RawTable {
206
242
capacity : capacity,
@@ -513,9 +549,9 @@ mod table {
513
549
assert ! ( self . size == 0 ) ;
514
550
515
551
unsafe {
516
- libc:: free ( self . vals as * mut libc:: c_void ) ;
517
- libc:: free ( self . keys as * mut libc:: c_void ) ;
518
552
libc:: free ( self . hashes as * mut libc:: c_void ) ;
553
+ // Remember how everything was allocated out of one buffer
554
+ // during initialization? We only need one call to free here.
519
555
}
520
556
}
521
557
}
0 commit comments