Add advance on Bytes and BytesMut (#166)

* Compact Bytes original capacity representation

In order to avoid unnecessary allocations, a `Bytes` structure remembers
the capacity with which it was first created. When a reserve operation
is issued, this original capacity value is used to as a baseline for
reallocating new storage.

Previously, this original capacity value was stored in its raw form. In
other words, the original capacity `usize` was stored as is. In order to
reclaim some `Bytes` internal storage space for additional features,
this original capacity value is compressed from requiring 16 bits to 3.

To do this, instead of storing the exact original capacity. The original
capacity is rounded down to the nearest power of two. If the original
capacity is less than 1024, then it is rounded down to zero. This
roughly means that the original capacity is now stored as a table:

0 => 0
1 => 1k
2 => 2k
3 => 4k
4 => 8k
5 => 16k
6 => 32k
7 => 64k

For the purposes that the original capacity feature was introduced, this
is sufficient granularity.

* Provide `advance` on Bytes and BytesMut

This is the `advance` function that would be part of a `Buf`
implementation. However, `Bytes` and `BytesMut` cannot impl `Buf` until
the next breaking release.

The implementation uses the additional storage made available by the
previous commit to store the number of bytes that the view was advanced.
The `ptr` pointer will point to the start of the window, avoiding any
pointer arithmetic when dereferencing the `Bytes` handle.
This commit is contained in:
Carl Lerche
2017-12-13 13:30:03 -06:00
committed by GitHub
parent 149922d7cf
commit 02891144be
2 changed files with 230 additions and 23 deletions
+41 -3
View File
@@ -350,16 +350,16 @@ fn reserve_growth() {
#[test]
fn reserve_allocates_at_least_original_capacity() {
let mut bytes = BytesMut::with_capacity(128);
let mut bytes = BytesMut::with_capacity(1024);
for i in 0..120 {
for i in 0..1020 {
bytes.put(i as u8);
}
let _other = bytes.take();
bytes.reserve(16);
assert_eq!(bytes.capacity(), 128);
assert_eq!(bytes.capacity(), 1024);
}
#[test]
@@ -466,6 +466,44 @@ fn from_static() {
assert_eq!(b, b"b"[..]);
}
#[test]
fn advance_inline() {
let mut a = Bytes::from(&b"hello world"[..]);
a.advance(6);
assert_eq!(a, &b"world"[..]);
}
#[test]
fn advance_static() {
let mut a = Bytes::from_static(b"hello world");
a.advance(6);
assert_eq!(a, &b"world"[..]);
}
#[test]
fn advance_vec() {
let mut a = BytesMut::from(b"hello world boooo yah world zomg wat wat".to_vec());
a.advance(16);
assert_eq!(a, b"o yah world zomg wat wat"[..]);
a.advance(4);
assert_eq!(a, b"h world zomg wat wat"[..]);
// Reserve some space.
a.reserve(1024);
assert_eq!(a, b"h world zomg wat wat"[..]);
a.advance(6);
assert_eq!(a, b"d zomg wat wat"[..]);
}
#[test]
#[should_panic]
fn advance_past_len() {
let mut a = BytesMut::from(b"hello world".to_vec());
a.advance(20);
}
#[test]
// Only run these tests on little endian systems. CI uses qemu for testing
// little endian... and qemu doesn't really support threading all that well.