@@ -240,6 +240,47 @@ impl<T> RawVec<T> {
240
240
}
241
241
}
242
242
243
+ /// Attempts to double the size of the type's backing allocation in place. This is common
244
+ /// enough to want to do that it's easiest to just have a dedicated method. Slightly
245
+ /// more efficient logic can be provided for this than the general case.
246
+ ///
247
+ /// Returns true if the reallocation attempt has succeeded, or false otherwise.
248
+ ///
249
+ /// # Panics
250
+ ///
251
+ /// * Panics if T is zero-sized on the assumption that you managed to exhaust
252
+ /// all `usize::MAX` slots in your imaginary buffer.
253
+ /// * Panics on 32-bit platforms if the requested capacity exceeds
254
+ /// `isize::MAX` bytes.
255
+ #[ inline( never) ]
256
+ #[ cold]
257
+ pub fn double_in_place ( & mut self ) -> bool {
258
+ unsafe {
259
+ let elem_size = mem:: size_of :: < T > ( ) ;
260
+ let align = mem:: align_of :: < T > ( ) ;
261
+
262
+ // since we set the capacity to usize::MAX when elem_size is
263
+ // 0, getting to here necessarily means the RawVec is overfull.
264
+ assert ! ( elem_size != 0 , "capacity overflow" ) ;
265
+
266
+ // Since we guarantee that we never allocate more than isize::MAX bytes,
267
+ // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow
268
+ let new_cap = 2 * self . cap ;
269
+ let new_alloc_size = new_cap * elem_size;
270
+
271
+ alloc_guard ( new_alloc_size) ;
272
+ let size = heap:: reallocate_inplace ( self . ptr ( ) as * mut _ ,
273
+ self . cap * elem_size,
274
+ new_alloc_size,
275
+ align) ;
276
+ if size >= new_alloc_size {
277
+ // We can't directly divide `size`.
278
+ self . cap = new_cap;
279
+ }
280
+ size >= new_alloc_size
281
+ }
282
+ }
283
+
243
284
/// Ensures that the buffer contains at least enough space to hold
244
285
/// `used_cap + needed_extra_cap` elements. If it doesn't already,
245
286
/// will reallocate the minimum possible amount of memory necessary.
@@ -300,6 +341,22 @@ impl<T> RawVec<T> {
300
341
}
301
342
}
302
343
344
+ /// Calculates the buffer's new size given that it'll hold `used_cap +
345
+ /// needed_extra_cap` elements. This logic is used in amortized reserve methods.
346
+ /// Returns `(new_capacity, new_alloc_size)`.
347
+ fn amortized_new_size ( & self , used_cap : usize , needed_extra_cap : usize ) -> ( usize , usize ) {
348
+ let elem_size = mem:: size_of :: < T > ( ) ;
349
+ // Nothing we can really do about these checks :(
350
+ let required_cap = used_cap. checked_add ( needed_extra_cap)
351
+ . expect ( "capacity overflow" ) ;
352
+ // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
353
+ let double_cap = self . cap * 2 ;
354
+ // `double_cap` guarantees exponential growth.
355
+ let new_cap = cmp:: max ( double_cap, required_cap) ;
356
+ let new_alloc_size = new_cap. checked_mul ( elem_size) . expect ( "capacity overflow" ) ;
357
+ ( new_cap, new_alloc_size)
358
+ }
359
+
303
360
/// Ensures that the buffer contains at least enough space to hold
304
361
/// `used_cap + needed_extra_cap` elements. If it doesn't already have
305
362
/// enough capacity, will reallocate enough space plus comfortable slack
@@ -360,17 +417,7 @@ impl<T> RawVec<T> {
360
417
return ;
361
418
}
362
419
363
- // Nothing we can really do about these checks :(
364
- let required_cap = used_cap. checked_add ( needed_extra_cap)
365
- . expect ( "capacity overflow" ) ;
366
-
367
- // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
368
- let double_cap = self . cap * 2 ;
369
-
370
- // `double_cap` guarantees exponential growth.
371
- let new_cap = cmp:: max ( double_cap, required_cap) ;
372
-
373
- let new_alloc_size = new_cap. checked_mul ( elem_size) . expect ( "capacity overflow" ) ;
420
+ let ( new_cap, new_alloc_size) = self . amortized_new_size ( used_cap, needed_extra_cap) ;
374
421
// FIXME: may crash and burn on over-reserve
375
422
alloc_guard ( new_alloc_size) ;
376
423
@@ -393,6 +440,55 @@ impl<T> RawVec<T> {
393
440
}
394
441
}
395
442
443
+ /// Attempts to ensure that the buffer contains at least enough space to hold
444
+ /// `used_cap + needed_extra_cap` elements. If it doesn't already have
445
+ /// enough capacity, will reallocate in place enough space plus comfortable slack
446
+ /// space to get amortized `O(1)` behaviour. Will limit this behaviour
447
+ /// if it would needlessly cause itself to panic.
448
+ ///
449
+ /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate
450
+ /// the requested space. This is not really unsafe, but the unsafe
451
+ /// code *you* write that relies on the behaviour of this function may break.
452
+ ///
453
+ /// Returns true if the reallocation attempt has succeeded, or false otherwise.
454
+ ///
455
+ /// # Panics
456
+ ///
457
+ /// * Panics if the requested capacity exceeds `usize::MAX` bytes.
458
+ /// * Panics on 32-bit platforms if the requested capacity exceeds
459
+ /// `isize::MAX` bytes.
460
+ pub fn reserve_in_place ( & mut self , used_cap : usize , needed_extra_cap : usize ) -> bool {
461
+ unsafe {
462
+ let elem_size = mem:: size_of :: < T > ( ) ;
463
+ let align = mem:: align_of :: < T > ( ) ;
464
+
465
+ // NOTE: we don't early branch on ZSTs here because we want this
466
+ // to actually catch "asking for more than usize::MAX" in that case.
467
+ // If we make it past the first branch then we are guaranteed to
468
+ // panic.
469
+
470
+ // Don't actually need any more capacity. If the current `cap` is 0, we can't
471
+ // reallocate in place.
472
+ // Wrapping in case they give a bad `used_cap`
473
+ if self . cap ( ) . wrapping_sub ( used_cap) >= needed_extra_cap || self . cap == 0 {
474
+ return false ;
475
+ }
476
+
477
+ let ( _, new_alloc_size) = self . amortized_new_size ( used_cap, needed_extra_cap) ;
478
+ // FIXME: may crash and burn on over-reserve
479
+ alloc_guard ( new_alloc_size) ;
480
+
481
+ let size = heap:: reallocate_inplace ( self . ptr ( ) as * mut _ ,
482
+ self . cap * elem_size,
483
+ new_alloc_size,
484
+ align) ;
485
+ if size >= new_alloc_size {
486
+ self . cap = new_alloc_size / elem_size;
487
+ }
488
+ size >= new_alloc_size
489
+ }
490
+ }
491
+
396
492
/// Shrinks the allocation down to the specified amount. If the given amount
397
493
/// is 0, actually completely deallocates.
398
494
///
0 commit comments