Skip to content

Commit 8cc9407

Browse files
authored
Allow reclaiming the current allocation (#686)
1 parent 7a5154b commit 8cc9407

File tree

2 files changed

+152
-7
lines changed

2 files changed

+152
-7
lines changed

src/bytes_mut.rs

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -597,12 +597,13 @@ impl BytesMut {
597597
return;
598598
}
599599

600-
self.reserve_inner(additional);
600+
// will always succeed
601+
let _ = self.reserve_inner(additional, true);
601602
}
602603

603-
// In separate function to allow the short-circuits in `reserve` to
604-
// be inline-able. Significant helps performance.
605-
fn reserve_inner(&mut self, additional: usize) {
604+
// In separate function to allow the short-circuits in `reserve` and `try_reclaim` to
605+
// be inline-able. Significantly helps performance. Returns false if it did not succeed.
606+
fn reserve_inner(&mut self, additional: usize, allocate: bool) -> bool {
606607
let len = self.len();
607608
let kind = self.kind();
608609

@@ -647,6 +648,9 @@ impl BytesMut {
647648
// can gain capacity back.
648649
self.cap += off;
649650
} else {
651+
if !allocate {
652+
return false;
653+
}
650654
// Not enough space, or reusing might be too much overhead:
651655
// allocate more space!
652656
let mut v =
@@ -659,7 +663,7 @@ impl BytesMut {
659663
debug_assert_eq!(self.len, v.len() - off);
660664
}
661665

662-
return;
666+
return true;
663667
}
664668
}
665669

@@ -670,7 +674,11 @@ impl BytesMut {
670674
// allocating a new vector with the requested capacity.
671675
//
672676
// Compute the new capacity
673-
let mut new_cap = len.checked_add(additional).expect("overflow");
677+
let mut new_cap = match len.checked_add(additional) {
678+
Some(new_cap) => new_cap,
679+
None if !allocate => return false,
680+
None => panic!("overflow"),
681+
};
674682

675683
unsafe {
676684
// First, try to reclaim the buffer. This is possible if the current
@@ -701,6 +709,9 @@ impl BytesMut {
701709
self.ptr = vptr(ptr);
702710
self.cap = v.capacity();
703711
} else {
712+
if !allocate {
713+
return false;
714+
}
704715
// calculate offset
705716
let off = (self.ptr.as_ptr() as usize) - (v.as_ptr() as usize);
706717

@@ -739,9 +750,12 @@ impl BytesMut {
739750
self.cap = v.capacity() - off;
740751
}
741752

742-
return;
753+
return true;
743754
}
744755
}
756+
if !allocate {
757+
return false;
758+
}
745759

746760
let original_capacity_repr = unsafe { (*shared).original_capacity_repr };
747761
let original_capacity = original_capacity_from_repr(original_capacity_repr);
@@ -764,6 +778,67 @@ impl BytesMut {
764778
self.ptr = vptr(v.as_mut_ptr());
765779
self.cap = v.capacity();
766780
debug_assert_eq!(self.len, v.len());
781+
return true;
782+
}
783+
784+
/// Attempts to cheaply reclaim already allocated capacity for at least `additional` more
785+
/// bytes to be inserted into the given `BytesMut` and returns `true` if it succeeded.
786+
///
787+
/// `try_reclaim` behaves exactly like `reserve`, except that it never allocates new storage
788+
/// and returns a `bool` indicating whether it was successful in doing so:
789+
///
790+
/// `try_reclaim` returns false under these conditions:
791+
/// - The spare capacity left is less than `additional` bytes AND
792+
/// - The existing allocation cannot be reclaimed cheaply or it was less than
793+
/// `additional` bytes in size
794+
///
795+
/// Reclaiming the allocation cheaply is possible if the `BytesMut` has no outstanding
796+
/// references through other `BytesMut`s or `Bytes` which point to the same underlying
797+
/// storage.
798+
///
799+
/// # Examples
800+
///
801+
/// ```
802+
/// use bytes::BytesMut;
803+
///
804+
/// let mut buf = BytesMut::with_capacity(64);
805+
/// assert_eq!(true, buf.try_reclaim(64));
806+
/// assert_eq!(64, buf.capacity());
807+
///
808+
/// buf.extend_from_slice(b"abcd");
809+
/// let mut split = buf.split();
810+
/// assert_eq!(60, buf.capacity());
811+
/// assert_eq!(4, split.capacity());
812+
/// assert_eq!(false, split.try_reclaim(64));
813+
/// assert_eq!(false, buf.try_reclaim(64));
814+
/// // The split buffer is filled with "abcd"
815+
/// assert_eq!(false, split.try_reclaim(4));
816+
/// // buf is empty and has capacity for 60 bytes
817+
/// assert_eq!(true, buf.try_reclaim(60));
818+
///
819+
/// drop(buf);
820+
/// assert_eq!(false, split.try_reclaim(64));
821+
///
822+
/// split.clear();
823+
/// assert_eq!(4, split.capacity());
824+
/// assert_eq!(true, split.try_reclaim(64));
825+
/// assert_eq!(64, split.capacity());
826+
/// ```
827+
// I tried splitting out try_reclaim_inner after the short circuits, but it was inlined
828+
// regardless with Rust 1.78.0 so probably not worth it
829+
#[inline]
830+
#[must_use = "consider BytesMut::reserve if you need an infallible reservation"]
831+
pub fn try_reclaim(&mut self, additional: usize) -> bool {
832+
let len = self.len();
833+
let rem = self.capacity() - len;
834+
835+
if additional <= rem {
836+
// The handle can already store at least `additional` more bytes, so
837+
// there is no further work needed to be done.
838+
return true;
839+
}
840+
841+
self.reserve_inner(additional, false)
767842
}
768843

769844
/// Appends given bytes to this `BytesMut`.

tests/test_bytes.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,3 +1283,73 @@ fn test_bytesmut_from_bytes_promotable_even_arc_offset() {
12831283
assert_eq!(b2m, vec[20..]);
12841284
assert_eq!(b1m, vec[..20]);
12851285
}
1286+
1287+
#[test]
1288+
fn try_reclaim_empty() {
1289+
let mut buf = BytesMut::new();
1290+
assert_eq!(false, buf.try_reclaim(6));
1291+
buf.reserve(6);
1292+
assert_eq!(true, buf.try_reclaim(6));
1293+
let cap = buf.capacity();
1294+
assert!(cap >= 6);
1295+
assert_eq!(false, buf.try_reclaim(cap + 1));
1296+
1297+
let mut buf = BytesMut::new();
1298+
buf.reserve(6);
1299+
let cap = buf.capacity();
1300+
assert!(cap >= 6);
1301+
let mut split = buf.split();
1302+
drop(buf);
1303+
assert_eq!(0, split.capacity());
1304+
assert_eq!(true, split.try_reclaim(6));
1305+
assert_eq!(false, split.try_reclaim(cap + 1));
1306+
}
1307+
1308+
#[test]
1309+
fn try_reclaim_vec() {
1310+
let mut buf = BytesMut::with_capacity(6);
1311+
buf.put_slice(b"abc");
1312+
// Reclaiming a ludicrous amount of space should calmly return false
1313+
assert_eq!(false, buf.try_reclaim(usize::MAX));
1314+
1315+
assert_eq!(false, buf.try_reclaim(6));
1316+
buf.advance(2);
1317+
assert_eq!(4, buf.capacity());
1318+
// We can reclaim 5 bytes, because the byte in the buffer can be moved to the front. 6 bytes
1319+
// cannot be reclaimed because there is already one byte stored
1320+
assert_eq!(false, buf.try_reclaim(6));
1321+
assert_eq!(true, buf.try_reclaim(5));
1322+
buf.advance(1);
1323+
assert_eq!(true, buf.try_reclaim(6));
1324+
assert_eq!(6, buf.capacity());
1325+
}
1326+
1327+
#[test]
1328+
fn try_reclaim_arc() {
1329+
let mut buf = BytesMut::with_capacity(6);
1330+
buf.put_slice(b"abc");
1331+
let x = buf.split().freeze();
1332+
buf.put_slice(b"def");
1333+
// Reclaiming a ludicrous amount of space should calmly return false
1334+
assert_eq!(false, buf.try_reclaim(usize::MAX));
1335+
1336+
let y = buf.split().freeze();
1337+
let z = y.clone();
1338+
assert_eq!(false, buf.try_reclaim(6));
1339+
drop(x);
1340+
drop(z);
1341+
assert_eq!(false, buf.try_reclaim(6));
1342+
drop(y);
1343+
assert_eq!(true, buf.try_reclaim(6));
1344+
assert_eq!(6, buf.capacity());
1345+
assert_eq!(0, buf.len());
1346+
buf.put_slice(b"abc");
1347+
buf.put_slice(b"def");
1348+
assert_eq!(6, buf.capacity());
1349+
assert_eq!(6, buf.len());
1350+
assert_eq!(false, buf.try_reclaim(6));
1351+
buf.advance(4);
1352+
assert_eq!(true, buf.try_reclaim(4));
1353+
buf.advance(2);
1354+
assert_eq!(true, buf.try_reclaim(6));
1355+
}

0 commit comments

Comments
 (0)