@@ -511,11 +511,20 @@ impl BytesMut {
511
511
/// reallocations. A call to `reserve` may result in an allocation.
512
512
///
513
513
/// Before allocating new buffer space, the function will attempt to reclaim
514
- /// space in the existing buffer. If the current handle references a small
515
- /// view in the original buffer and all other handles have been dropped,
516
- /// and the requested capacity is less than or equal to the existing
517
- /// buffer's capacity, then the current view will be copied to the front of
518
- /// the buffer and the handle will take ownership of the full buffer.
514
+ /// space in the existing buffer. If the current handle references a view
515
+ /// into a larger original buffer, and all other handles referencing part
516
+ /// of the same original buffer have been dropped, then the current view
517
+ /// can be copied/shifted to the front of the buffer and the handle can take
518
+ /// ownership of the full buffer, provided that the full buffer is large
519
+ /// enough to fit the requested additional capacity.
520
+ ///
521
+ /// This optimization will only happen if shifting the data from the current
522
+ /// view to the front of the buffer is not too expensive in terms of the
523
+ /// (amortized) time required. The precise condition is subject to change;
524
+ /// as of now, the length of the data being shifted needs to be at least as
525
+ /// large as the distance that it's shifted by. If the current view is empty
526
+ /// and the original buffer is large enough to fit the requested additional
527
+ /// capacity, then reallocations will never happen.
519
528
///
520
529
/// # Examples
521
530
///
@@ -579,25 +588,43 @@ impl BytesMut {
579
588
// space.
580
589
//
581
590
// Otherwise, since backed by a vector, use `Vec::reserve`
591
+ //
592
+ // We need to make sure that this optimization does not kill the
593
+ // amortized runtimes of BytesMut's operations.
582
594
unsafe {
583
595
let ( off, prev) = self . get_vec_pos ( ) ;
584
596
585
597
// Only reuse space if we can satisfy the requested additional space.
586
- if self . capacity ( ) - self . len ( ) + off >= additional {
587
- // There's space - reuse it
598
+ //
599
+ // Also check if the value of `off` suggests that enough bytes
600
+ // have been read to account for the overhead of shifting all
601
+ // the data (in an amortized analysis).
602
+ // Hence the condition `off >= self.len()`.
603
+ //
604
+ // This condition also already implies that the buffer is going
605
+ // to be (at least) half-empty in the end; so we do not break
606
+ // the (amortized) runtime with future resizes of the underlying
607
+ // `Vec`.
608
+ //
609
+ // [For more details check issue #524, and PR #525.]
610
+ if self . capacity ( ) - self . len ( ) + off >= additional && off >= self . len ( ) {
611
+ // There's enough space, and it's not too much overhead:
612
+ // reuse the space!
588
613
//
589
614
// Just move the pointer back to the start after copying
590
615
// data back.
591
616
let base_ptr = self . ptr . as_ptr ( ) . offset ( -( off as isize ) ) ;
592
- ptr:: copy ( self . ptr . as_ptr ( ) , base_ptr, self . len ) ;
617
+ // Since `off >= self.len()`, the two regions don't overlap.
618
+ ptr:: copy_nonoverlapping ( self . ptr . as_ptr ( ) , base_ptr, self . len ) ;
593
619
self . ptr = vptr ( base_ptr) ;
594
620
self . set_vec_pos ( 0 , prev) ;
595
621
596
622
// Length stays constant, but since we moved backwards we
597
623
// can gain capacity back.
598
624
self . cap += off;
599
625
} else {
600
- // No space - allocate more
626
+ // Not enough space, or reusing might be too much overhead:
627
+ // allocate more space!
601
628
let mut v =
602
629
ManuallyDrop :: new ( rebuild_vec ( self . ptr . as_ptr ( ) , self . len , self . cap , off) ) ;
603
630
v. reserve ( additional) ;
@@ -636,11 +663,19 @@ impl BytesMut {
636
663
// sure that the vector has enough capacity.
637
664
let v = & mut ( * shared) . vec ;
638
665
639
- if v. capacity ( ) >= new_cap {
640
- // The capacity is sufficient, reclaim the buffer
641
- let ptr = v. as_mut_ptr ( ) ;
666
+ let v_capacity = v. capacity ( ) ;
667
+ let ptr = v. as_mut_ptr ( ) ;
668
+
669
+ let offset = offset_from ( self . ptr . as_ptr ( ) , ptr) ;
642
670
643
- ptr:: copy ( self . ptr . as_ptr ( ) , ptr, len) ;
671
+ // Compare the condition in the `kind == KIND_VEC` case above
672
+ // for more details.
673
+ if v_capacity >= new_cap && offset >= len {
674
+ // The capacity is sufficient, and copying is not too much
675
+ // overhead: reclaim the buffer!
676
+
677
+ // `offset >= len` means: no overlap
678
+ ptr:: copy_nonoverlapping ( self . ptr . as_ptr ( ) , ptr, len) ;
644
679
645
680
self . ptr = vptr ( ptr) ;
646
681
self . cap = v. capacity ( ) ;
@@ -1294,7 +1329,7 @@ unsafe fn release_shared(ptr: *mut Shared) {
1294
1329
( * ptr) . ref_count . load ( Ordering :: Acquire ) ;
1295
1330
1296
1331
// Drop the data
1297
- Box :: from_raw ( ptr) ;
1332
+ drop ( Box :: from_raw ( ptr) ) ;
1298
1333
}
1299
1334
1300
1335
impl Shared {
@@ -1599,6 +1634,23 @@ fn invalid_ptr<T>(addr: usize) -> *mut T {
1599
1634
ptr. cast :: < T > ( )
1600
1635
}
1601
1636
1637
+ /// Precondition: dst >= original
1638
+ ///
1639
+ /// The following line is equivalent to:
1640
+ ///
1641
+ /// ```rust,ignore
1642
+ /// self.ptr.as_ptr().offset_from(ptr) as usize;
1643
+ /// ```
1644
+ ///
1645
+ /// But due to min rust is 1.39 and it is only stablised
1646
+ /// in 1.47, we cannot use it.
1647
+ #[ inline]
1648
+ fn offset_from ( dst : * mut u8 , original : * mut u8 ) -> usize {
1649
+ debug_assert ! ( dst >= original) ;
1650
+
1651
+ dst as usize - original as usize
1652
+ }
1653
+
1602
1654
unsafe fn rebuild_vec ( ptr : * mut u8 , mut len : usize , mut cap : usize , off : usize ) -> Vec < u8 > {
1603
1655
let ptr = ptr. offset ( -( off as isize ) ) ;
1604
1656
len += off;
0 commit comments