Compare commits

...
35 Commits
Author SHA1 Message Date
Alice RyhlandGitHub 38fd42acba chore: prepare bytes v1.2.0 (#556) 2022-07-19 13:39:58 +02:00
7553a67be2 Fix amortized asymptotics of BytesMut (#555)
Signed-off-by: Jiahao XU <[email protected]>
Co-authored-by: Frank Steffahn <[email protected]>
2022-07-19 13:17:53 +02:00
cd188cbd67 Add conversion from Bytes to Vec<u8> (#547)
Signed-off-by: Jiahao XU <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2022-07-13 09:04:23 +02:00
Jiahao XUandGitHub 10d1f6ec5c Fix: From<BytesMut> fo Vec<u8> implementation (#554)
Signed-off-by: Jiahao XU <[email protected]>
2022-07-13 08:59:54 +02:00
Jiahao XUandGitHub 068ed41bc0 Add conversion from BytesMut to Vec<u8> (#543) 2022-07-10 12:44:29 +02:00
Alice RyhlandGitHub f514bd38da miri: don't use int2ptr casts for invalid pointers (#553) 2022-07-09 21:54:34 +02:00
Lucio FrancoandGitHub 28a1eab1e0 chore: Fix unused warnings (#551) 2022-06-23 09:16:03 -07:00
ZettrokeandGitHub 3536017ccf Fix chain remaining_mut(), allowing to chain growing buffer (#488) 2022-06-11 13:12:53 +09:00
Erick TryzelaarandGitHub b8d27c016f Add UninitSlice::as_uninit_slice_mut() (#548)
This adds an unsafe method to convert a `&mut UninitSlice` into a
`&mut [MaybeUninit<u8>]`. This method is unsafe because some of the
bytes in the slice may be initialized, and the caller should not
overwrite them with uninitialized bytes.

This came about when auditing [tokio-util's udp frame], where they want
to pass the unitialized portion of a `BytesMut` to [ReadBuf::uninit].
They need to do this unsafe pointer casting in a few places, which
complicates audits. This method lets us document the safety invariants
the caller needs to maintain when doing this conversion.

[tokio-util's udp frame]: https://github.com/tokio-rs/tokio/blob/master/tokio-util/src/udp/frame.rs#L87
[ReadBuf::uninit]: https://docs.rs/tokio/latest/tokio/io/struct.ReadBuf.html#method.uninit
2022-05-10 11:22:19 +02:00
Taiki EndoandGitHub 0ce4fe3c91 Update actions/checkout action to v3 (#546) 2022-05-01 14:50:55 +02:00
Alice RyhlandGitHub 716a0b189e Only avoid pointer casts when using miri (#545) 2022-04-29 22:01:26 +02:00
Alice Ryhl b4b2c18c27 Revert accidental push directly to master
This reverts commit 89061c3238.

Why am I even able to push to master?
2022-04-29 19:49:28 +02:00
Alice Ryhl 89061c3238 Only avoid pointer casts when using miri 2022-04-29 19:47:34 +02:00
Jiahao XUandGitHub 0a2c43af88 Fix bugs in BytesMut::reserve_inner (#544) 2022-04-28 11:37:33 +02:00
Alice RyhlandGitHub 8198f9e28e Make strict provenance compatible (#542) 2022-04-16 00:26:58 +02:00
Alice RyhlandGitHub 547a32033e Add TSAN support (#541) 2022-04-15 22:46:40 +02:00
Ben KimockandGitHub 724476982b Fix aliasing in Clone by using a raw pointer (#523)
Previously, this code produced a &mut[u8] and a Box<[u8]> to the shared
allocation upon cloning it. If the underlying allocation were actually
shared, such as through a &[u8] from the Deref impl, creating either of
these types incorrectly asserted uniqueness of the allocation.

This fixes the example in #522, but Miri still does not pass on this
test suite with -Zmiri-tag-raw-pointers because Miri does not currently
understand int to pointer casts.
2022-04-06 16:59:20 +02:00
Evan CameronandGitHub 9e6edd18d2 Clarify BytesMut::unsplit docs (#535) 2022-04-06 15:19:00 +02:00
Anthony DeschampsandGitHub e4c723697d docs: redraw layout diagram with box drawing characters. (#539)
I find this diagram very helpful, but a little hard to distinguish
between the boxes and the lines that connect them. This commit redraws
the boxes with line drawing characters so that the boxes appear a
little more solid, and stand out from the other lines.
2022-03-25 10:55:13 +01:00
Jiahao XUandGitHub d4f5023383 Optimize BytesMut::reserve: Reuse vec if possible (#529)
* Optimize `BytesMut::reserve`: Reuse vec if possible

If the `BytesMut` holds a unqiue reference to `KIND_ARC` while the
capacity of the `Vec` is not big enough , reuse the existing `Vec`
instead of allocating a new one.

Signed-off-by: Jiahao XU <[email protected]>
2022-03-16 10:11:42 -04:00
Ralf JungandGitHub 88f5e12350 update Miri CI config (#534) 2022-03-09 10:29:04 +01:00
Rob EdeandGitHub 131dae161f Implement Extend<Bytes> for BytesMut (#527) 2022-01-24 09:58:18 +01:00
Rob EdeandGitHub 0e3b2466f1 Address various clippy warnings (#528) 2022-01-24 09:58:05 +01:00
Donough LiuandGitHub 68afb40df9 Add BytesMut::zeroed (#517) 2021-11-24 10:20:21 +01:00
Cyborus04andGitHub d946ef2e91 const-ify Bytes::len and Bytes::is_empty (#514) 2021-11-09 11:41:40 +01:00
Noah KennedyandGitHub ba5c5c93af Appease miri (#515)
Rewrote the ledger in test_bytes_vec_alloc.rs to not piss off miri. The ledger is now a table within the allocator, which seems to satisfy miri. The old solution was to bundle an extra usize into the beginning of each allocation and then index past the start when deallocating data to get the size.
2021-11-07 10:23:12 -06:00
Alice RyhlandGitHub ebc61e5af1 chore: prepare bytes v1.1.0 (#509) 2021-08-25 17:48:41 +02:00
Christopher HotchkissandGitHub 55e296850d Clarifying actions of clear and truncate. (#508) 2021-08-24 16:12:20 +02:00
Ian JacksonandGitHub 0e9fa0b602 impl From<Box<[u8]>> for Bytes (#504) 2021-08-24 12:42:22 +02:00
ee24be7fa0 ci: fetch cargo hack from github release (#507)
Co-authored-by: Taiki Endo <[email protected]>
2021-08-24 12:33:16 +02:00
Stepan KoltsovandGitHub 2697fa7a9d BufMut::put_bytes(self, val, cnt) (#487)
Equivalent to

```
for _ in 0..cnt {
    self.put_u8(val);
}
```

but may work faster.

Name and signature is chosen to be consistent with `ptr::write_bytes`.

Include three specializations:
* `Vec<u8>`
* `&mut [u8]`
* `BytesMut`

`BytesMut` and `&mut [u8]` specializations use `ptr::write`, `Vec<u8>`
specialization uses `Vec::resize`.
2021-08-09 02:43:53 +09:00
Stepan KoltsovandGitHub fa9cbf1258 Clarify BufPut::put_int behavior (#486)
* writes low bytes, discards high bytes
* panics if `nbytes` is greater than 8
2021-08-09 02:43:09 +09:00
Alice RyhlandGitHub ab8e3c01a8 Clarify BufMut allocation guarantees (#501) 2021-08-07 08:22:18 +02:00
Taiki EndoandGitHub f34dc5c3f9 Remove doc URLs (#498) 2021-08-07 02:06:57 +09:00
GbillouandGitHub baaf12d22a Keep capacity when unsplit on empty other buf (#502) 2021-07-05 16:46:17 +02:00
21 changed files with 976 additions and 188 deletions
+11 -11
View File
@@ -11,7 +11,7 @@ on:
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
nightly: nightly-2021-04-13
nightly: nightly-2021-11-05
defaults:
run:
@@ -23,7 +23,7 @@ jobs:
name: rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update stable && rustup default stable
- name: Check formatting
@@ -35,7 +35,7 @@ jobs:
# name: clippy
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - uses: actions/checkout@v3
# - name: Apply clippy lints
# run: cargo clippy --all-features
@@ -48,7 +48,7 @@ jobs:
name: minrust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update 1.39.0 && rustup default 1.39.0
- name: Check
@@ -65,7 +65,7 @@ jobs:
- windows-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
# --no-self-update is necessary because the windows environment cannot self-update rustup.exe.
run: rustup update stable --no-self-update && rustup default stable
@@ -77,7 +77,7 @@ jobs:
name: nightly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Test
@@ -96,7 +96,7 @@ jobs:
- wasm32-unknown-unknown
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update stable && rustup default stable
- name: cross build --target ${{ matrix.target }}
@@ -116,7 +116,7 @@ jobs:
name: tsan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Install rust-src
@@ -127,7 +127,7 @@ jobs:
name: miri
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Miri
run: ci/miri.sh
@@ -136,7 +136,7 @@ jobs:
name: loom
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Loom tests
@@ -155,7 +155,7 @@ jobs:
- loom
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update stable && rustup default stable
- name: Build documentation
+48
View File
@@ -1,3 +1,51 @@
# 1.2.0 (July 19, 2022)
### Added
- Add `BytesMut::zeroed` (#517)
- Implement `Extend<Bytes>` for `BytesMut` (#527)
- Add conversion from `BytesMut` to `Vec<u8>` (#543, #554)
- Add conversion from `Bytes` to `Vec<u8>` (#547)
- Add `UninitSlice::as_uninit_slice_mut()` (#548)
- Add const to `Bytes::{len,is_empty}` (#514)
### Changed
- Reuse vector in `BytesMut::reserve` (#539, #544)
### Fixed
- Make miri happy (#515, #523, #542, #545, #553)
- Make tsan happy (#541)
- Fix `remaining_mut()` on chain (#488)
- Fix amortized asymptotics of `BytesMut` (#555)
### Documented
- Redraw layout diagram with box drawing characters (#539)
- Clarify `BytesMut::unsplit` docs (#535)
# 1.1.0 (August 25, 2021)
### Added
- `BufMut::put_bytes(self, val, cnt)` (#487)
- Implement `From<Box<[u8]>>` for `Bytes` (#504)
### Changed
- Override `put_slice` for `&mut [u8]` (#483)
- Panic on integer overflow in `Chain::remaining` (#482)
- Add inline tags to `UninitSlice` methods (#443)
- Override `copy_to_bytes` for Chain and Take (#481)
- Keep capacity when unsplit on empty other buf (#502)
### Documented
- Clarify `BufMut` allocation guarantees (#501)
- Clarify `BufMut::put_int` behavior (#486)
- Clarify actions of `clear` and `truncate`. (#508)
# 1.0.1 (January 11, 2021)
### Changed
+2 -5
View File
@@ -2,18 +2,15 @@
name = "bytes"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Update doc URL.
# - Create "v1.0.x" git tag.
version = "1.0.1"
# - Create "v1.x.y" git tag.
version = "1.2.0"
license = "MIT"
authors = [
"Carl Lerche <[email protected]>",
"Sean McArthur <[email protected]>",
]
description = "Types and traits for working with bytes"
documentation = "https://docs.rs/bytes/1.0.1/bytes/"
repository = "https://github.com/tokio-rs/bytes"
readme = "README.md"
keywords = ["buffers", "zero-copy", "io"]
+1 -1
View File
@@ -46,7 +46,7 @@ impl TestBuf {
}
impl Buf for TestBuf {
fn remaining(&self) -> usize {
return self.buf.len() - self.pos;
self.buf.len() - self.pos
}
fn advance(&mut self, cnt: usize) {
self.pos += cnt;
+1
View File
@@ -88,6 +88,7 @@ fn from_long_slice(b: &mut Bencher) {
#[bench]
fn slice_empty(b: &mut Bencher) {
b.iter(|| {
// `clone` is to convert to ARC
let b = Bytes::from(vec![17; 1024]).clone();
for i in 0..1000 {
test::black_box(b.slice(i % 100..i % 100));
+5 -5
View File
@@ -1,11 +1,11 @@
#!/bin/bash
set -e
MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)
echo "Installing latest nightly with Miri: $MIRI_NIGHTLY"
rustup set profile minimal
rustup default "$MIRI_NIGHTLY"
rustup component add miri
rustup toolchain install nightly --component miri
rustup override set nightly
cargo miri setup
export MIRIFLAGS="-Zmiri-strict-provenance"
cargo miri test
cargo miri test --target mips64-unknown-linux-gnuabi64
+2 -1
View File
@@ -5,7 +5,8 @@ set -ex
cmd="${1:-test}"
# Install cargo-hack for feature flag test
cargo install cargo-hack
host=$(rustc -Vv | grep host | sed 's/host: //')
curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-$host.tar.gz | tar xzf - -C ~/.cargo/bin
# Run with each feature
# * --each-feature includes both default/no-default features
+1
View File
@@ -0,0 +1 @@
msrv = "1.39"
+63 -7
View File
@@ -33,6 +33,10 @@ pub unsafe trait BufMut {
/// This value is greater than or equal to the length of the slice returned
/// by `chunk_mut()`.
///
/// Writing to a `BufMut` may involve allocating more memory on the fly.
/// Implementations may fail before reaching the number of bytes indicated
/// by this method if they encounter an allocation failure.
///
/// # Examples
///
/// ```
@@ -52,6 +56,10 @@ pub unsafe trait BufMut {
/// Implementations of `remaining_mut` should ensure that the return value
/// does not change unless a call is made to `advance_mut` or any other
/// function that is documented to change the `BufMut`'s current position.
///
/// # Note
///
/// `remaining_mut` may return value smaller than actual available space.
fn remaining_mut(&self) -> usize;
/// Advance the internal cursor of the BufMut
@@ -158,6 +166,9 @@ pub unsafe trait BufMut {
/// `chunk_mut()` returning an empty slice implies that `remaining_mut()` will
/// return 0 and `remaining_mut()` returning 0 implies that `chunk_mut()` will
/// return an empty slice.
///
/// This function may trigger an out-of-memory abort if it tries to allocate
/// memory and fails to do so.
// The `chunk_mut` method was previously called `bytes_mut`. This alias makes the
// rename more easily discoverable.
#[cfg_attr(docsrs, doc(alias = "bytes_mut"))]
@@ -254,6 +265,37 @@ pub unsafe trait BufMut {
}
}
/// Put `cnt` bytes `val` into `self`.
///
/// Logically equivalent to calling `self.put_u8(val)` `cnt` times, but may work faster.
///
/// `self` must have at least `cnt` remaining capacity.
///
/// ```
/// use bytes::BufMut;
///
/// let mut dst = [0; 6];
///
/// {
/// let mut buf = &mut dst[..];
/// buf.put_bytes(b'a', 4);
///
/// assert_eq!(2, buf.remaining_mut());
/// }
///
/// assert_eq!(b"aaaa\0\0", &dst);
/// ```
///
/// # Panics
///
/// This function panics if there is not enough remaining capacity in
/// `self`.
fn put_bytes(&mut self, val: u8, cnt: usize) {
for _ in 0..cnt {
self.put_u8(val);
}
}
/// Writes an unsigned 8 bit integer to `self`.
///
/// The current position is advanced by 1.
@@ -696,7 +738,7 @@ pub unsafe trait BufMut {
self.put_slice(&n.to_le_bytes()[0..nbytes]);
}
/// Writes a signed n-byte integer to `self` in big-endian byte order.
/// Writes low `nbytes` of a signed integer to `self` in big-endian byte order.
///
/// The current position is advanced by `nbytes`.
///
@@ -706,19 +748,19 @@ pub unsafe trait BufMut {
/// use bytes::BufMut;
///
/// let mut buf = vec![];
/// buf.put_int(0x010203, 3);
/// buf.put_int(0x0504010203, 3);
/// assert_eq!(buf, b"\x01\x02\x03");
/// ```
///
/// # Panics
///
/// This function panics if there is not enough remaining capacity in
/// `self`.
/// `self` or if `nbytes` is greater than 8.
fn put_int(&mut self, n: i64, nbytes: usize) {
self.put_slice(&n.to_be_bytes()[mem::size_of_val(&n) - nbytes..]);
}
/// Writes a signed n-byte integer to `self` in little-endian byte order.
/// Writes low `nbytes` of a signed integer to `self` in little-endian byte order.
///
/// The current position is advanced by `nbytes`.
///
@@ -728,14 +770,14 @@ pub unsafe trait BufMut {
/// use bytes::BufMut;
///
/// let mut buf = vec![];
/// buf.put_int_le(0x010203, 3);
/// buf.put_int_le(0x0504010203, 3);
/// assert_eq!(buf, b"\x03\x02\x01");
/// ```
///
/// # Panics
///
/// This function panics if there is not enough remaining capacity in
/// `self`.
/// `self` or if `nbytes` is greater than 8.
fn put_int_le(&mut self, n: i64, nbytes: usize) {
self.put_slice(&n.to_le_bytes()[0..nbytes]);
}
@@ -1020,12 +1062,21 @@ unsafe impl BufMut for &mut [u8] {
self.advance_mut(src.len());
}
}
fn put_bytes(&mut self, val: u8, cnt: usize) {
assert!(self.remaining_mut() >= cnt);
unsafe {
ptr::write_bytes(self.as_mut_ptr(), val, cnt);
self.advance_mut(cnt);
}
}
}
unsafe impl BufMut for Vec<u8> {
#[inline]
fn remaining_mut(&self) -> usize {
usize::MAX - self.len()
// A vector can never have more than isize::MAX bytes
core::isize::MAX as usize - self.len()
}
#[inline]
@@ -1083,6 +1134,11 @@ unsafe impl BufMut for Vec<u8> {
fn put_slice(&mut self, src: &[u8]) {
self.extend_from_slice(src);
}
fn put_bytes(&mut self, val: u8, cnt: usize) {
let new_len = self.len().checked_add(cnt).unwrap();
self.resize(new_len, val);
}
}
// The existence of this function makes the compiler catch if the BufMut
+1 -2
View File
@@ -198,8 +198,7 @@ where
fn remaining_mut(&self) -> usize {
self.a
.remaining_mut()
.checked_add(self.b.remaining_mut())
.unwrap()
.saturating_add(self.b.remaining_mut())
}
fn chunk_mut(&mut self) -> &mut UninitSlice {
+26
View File
@@ -124,6 +124,32 @@ impl UninitSlice {
self.0.as_mut_ptr() as *mut _
}
/// Return a `&mut [MaybeUninit<u8>]` to this slice's buffer.
///
/// # Safety
///
/// The caller **must not** read from the referenced memory and **must not** write
/// **uninitialized** bytes to the slice either. This is because `BufMut` implementation
/// that created the `UninitSlice` knows which parts are initialized. Writing uninitalized
/// bytes to the slice may cause the `BufMut` to read those bytes and trigger undefined
/// behavior.
///
/// # Examples
///
/// ```
/// use bytes::BufMut;
///
/// let mut data = [0, 1, 2];
/// let mut slice = &mut data[..];
/// unsafe {
/// let uninit_slice = BufMut::chunk_mut(&mut slice).as_uninit_slice_mut();
/// };
/// ```
#[inline]
pub unsafe fn as_uninit_slice_mut<'a>(&'a mut self) -> &'a mut [MaybeUninit<u8>] {
&mut *(self as *mut _ as *mut [MaybeUninit<u8>])
}
/// Returns the number of bytes in the slice.
///
/// # Examples
+184 -46
View File
@@ -2,12 +2,18 @@ use core::iter::FromIterator;
use core::ops::{Deref, RangeBounds};
use core::{cmp, fmt, hash, mem, ptr, slice, usize};
use alloc::{borrow::Borrow, boxed::Box, string::String, vec::Vec};
use alloc::{
alloc::{dealloc, Layout},
borrow::Borrow,
boxed::Box,
string::String,
vec::Vec,
};
use crate::buf::IntoIter;
#[allow(unused)]
use crate::loom::sync::atomic::AtomicMut;
use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::Buf;
/// A cheaply cloneable and sliceable chunk of contiguous memory.
@@ -55,7 +61,7 @@ use crate::Buf;
/// # Sharing
///
/// `Bytes` contains a vtable, which allows implementations of `Bytes` to define
/// how sharing/cloneing is implemented in detail.
/// how sharing/cloning is implemented in detail.
/// When `Bytes::clone()` is called, `Bytes` will call the vtable function for
/// cloning the backing storage in order to share it behind between multiple
/// `Bytes` instances.
@@ -78,18 +84,18 @@ use crate::Buf;
///
/// ```text
///
/// Arc ptrs +---------+
/// ________________________ / | Bytes 2 |
/// / +---------+
/// / +-----------+ | |
/// |_________/ | Bytes 1 | | |
/// | +-----------+ | |
/// Arc ptrs ┌─────────┐
/// ________________________ / Bytes 2
/// / └─────────┘
/// / ┌───────────┐ | |
/// |_________/ Bytes 1 | |
/// | └───────────┘ | |
/// | | | ___/ data | tail
/// | data | tail |/ |
/// v v v v
/// +-----+---------------------------------+-----+
/// | Arc | | | | |
/// +-----+---------------------------------+-----+
/// ┌─────┬─────┬───────────┬───────────────┬─────┐
/// Arc
/// └─────┴─────┴───────────┴───────────────┴─────┘
/// ```
pub struct Bytes {
ptr: *const u8,
@@ -103,6 +109,10 @@ pub(crate) struct Vtable {
/// fn(data, ptr, len)
pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Bytes,
/// fn(data, ptr, len)
///
/// takes `Bytes` to value
pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec<u8>,
/// fn(data, ptr, len)
pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize),
}
@@ -179,7 +189,7 @@ impl Bytes {
/// assert_eq!(b.len(), 5);
/// ```
#[inline]
pub fn len(&self) -> usize {
pub const fn len(&self) -> usize {
self.len
}
@@ -194,7 +204,7 @@ impl Bytes {
/// assert!(b.is_empty());
/// ```
#[inline]
pub fn is_empty(&self) -> bool {
pub const fn is_empty(&self) -> bool {
self.len == 0
}
@@ -262,7 +272,7 @@ impl Bytes {
let mut ret = self.clone();
ret.len = end - begin;
ret.ptr = unsafe { ret.ptr.offset(begin as isize) };
ret.ptr = unsafe { ret.ptr.add(begin) };
ret
}
@@ -308,15 +318,15 @@ impl Bytes {
assert!(
sub_p >= bytes_p,
"subset pointer ({:p}) is smaller than self pointer ({:p})",
sub_p as *const u8,
bytes_p as *const u8,
subset.as_ptr(),
self.as_ptr(),
);
assert!(
sub_p + sub_len <= bytes_p + bytes_len,
"subset is out of bounds: self = ({:p}, {}), subset = ({:p}, {})",
bytes_p as *const u8,
self.as_ptr(),
bytes_len,
sub_p as *const u8,
subset.as_ptr(),
sub_len,
);
@@ -501,7 +511,7 @@ impl Bytes {
// should already be asserted, but debug assert for tests
debug_assert!(self.len >= by, "internal: inc_start out of bounds");
self.len -= by;
self.ptr = self.ptr.offset(by as isize);
self.ptr = self.ptr.add(by);
}
}
@@ -604,7 +614,7 @@ impl<'a> IntoIterator for &'a Bytes {
type IntoIter = core::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.as_slice().into_iter()
self.as_slice().iter()
}
}
@@ -686,7 +696,7 @@ impl PartialOrd<Bytes> for str {
impl PartialEq<Vec<u8>> for Bytes {
fn eq(&self, other: &Vec<u8>) -> bool {
*self == &other[..]
*self == other[..]
}
}
@@ -710,7 +720,7 @@ impl PartialOrd<Bytes> for Vec<u8> {
impl PartialEq<String> for Bytes {
fn eq(&self, other: &String) -> bool {
*self == &other[..]
*self == other[..]
}
}
@@ -797,30 +807,36 @@ impl From<&'static str> for Bytes {
impl From<Vec<u8>> for Bytes {
fn from(vec: Vec<u8>) -> Bytes {
// into_boxed_slice doesn't return a heap allocation for empty vectors,
let slice = vec.into_boxed_slice();
slice.into()
}
}
impl From<Box<[u8]>> for Bytes {
fn from(slice: Box<[u8]>) -> Bytes {
// Box<[u8]> doesn't contain a heap allocation for empty slices,
// so the pointer isn't aligned enough for the KIND_VEC stashing to
// work.
if vec.is_empty() {
if slice.is_empty() {
return Bytes::new();
}
let slice = vec.into_boxed_slice();
let len = slice.len();
let ptr = Box::into_raw(slice) as *mut u8;
if ptr as usize & 0x1 == 0 {
let data = ptr as usize | KIND_VEC;
let data = ptr_map(ptr, |addr| addr | KIND_VEC);
Bytes {
ptr,
len,
data: AtomicPtr::new(data as *mut _),
data: AtomicPtr::new(data.cast()),
vtable: &PROMOTABLE_EVEN_VTABLE,
}
} else {
Bytes {
ptr,
len,
data: AtomicPtr::new(ptr as *mut _),
data: AtomicPtr::new(ptr.cast()),
vtable: &PROMOTABLE_ODD_VTABLE,
}
}
@@ -833,6 +849,13 @@ impl From<String> for Bytes {
}
}
impl From<Bytes> for Vec<u8> {
fn from(bytes: Bytes) -> Vec<u8> {
let bytes = mem::ManuallyDrop::new(bytes);
unsafe { (bytes.vtable.to_vec)(&bytes.data, bytes.ptr, bytes.len) }
}
}
// ===== impl Vtable =====
impl fmt::Debug for Vtable {
@@ -848,6 +871,7 @@ impl fmt::Debug for Vtable {
const STATIC_VTABLE: Vtable = Vtable {
clone: static_clone,
to_vec: static_to_vec,
drop: static_drop,
};
@@ -856,6 +880,11 @@ unsafe fn static_clone(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
Bytes::from_static(slice)
}
unsafe fn static_to_vec(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
let slice = slice::from_raw_parts(ptr, len);
slice.to_vec()
}
unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
// nothing to drop for &'static [u8]
}
@@ -864,11 +893,13 @@ unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable {
clone: promotable_even_clone,
to_vec: promotable_even_to_vec,
drop: promotable_even_drop,
};
static PROMOTABLE_ODD_VTABLE: Vtable = Vtable {
clone: promotable_odd_clone,
to_vec: promotable_odd_to_vec,
drop: promotable_odd_drop,
};
@@ -877,25 +908,57 @@ unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
shallow_clone_arc(shared as _, ptr, len)
shallow_clone_arc(shared.cast(), ptr, len)
} else {
debug_assert_eq!(kind, KIND_VEC);
let buf = (shared as usize & !KIND_MASK) as *mut u8;
let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK);
shallow_clone_vec(data, shared, buf, ptr, len)
}
}
unsafe fn promotable_to_vec(
data: &AtomicPtr<()>,
ptr: *const u8,
len: usize,
f: fn(*mut ()) -> *mut u8,
) -> Vec<u8> {
let shared = data.load(Ordering::Acquire);
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
shared_to_vec_impl(shared.cast(), ptr, len)
} else {
// If Bytes holds a Vec, then the offset must be 0.
debug_assert_eq!(kind, KIND_VEC);
let buf = f(shared);
let cap = (ptr as usize - buf as usize) + len;
// Copy back buffer
ptr::copy(ptr, buf, len);
Vec::from_raw_parts(buf, len, cap)
}
}
unsafe fn promotable_even_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
promotable_to_vec(data, ptr, len, |shared| {
ptr_map(shared.cast(), |addr| addr & !KIND_MASK)
})
}
unsafe fn promotable_even_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) {
data.with_mut(|shared| {
let shared = *shared;
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
release_shared(shared as *mut Shared);
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
let buf = (shared as usize & !KIND_MASK) as *mut u8;
drop(rebuild_boxed_slice(buf, ptr, len));
let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK);
free_boxed_slice(buf, ptr, len);
}
});
}
@@ -908,38 +971,49 @@ unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize)
shallow_clone_arc(shared as _, ptr, len)
} else {
debug_assert_eq!(kind, KIND_VEC);
shallow_clone_vec(data, shared, shared as *mut u8, ptr, len)
shallow_clone_vec(data, shared, shared.cast(), ptr, len)
}
}
unsafe fn promotable_odd_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
promotable_to_vec(data, ptr, len, |shared| shared.cast())
}
unsafe fn promotable_odd_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) {
data.with_mut(|shared| {
let shared = *shared;
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
release_shared(shared as *mut Shared);
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
drop(rebuild_boxed_slice(shared as *mut u8, ptr, len));
free_boxed_slice(shared.cast(), ptr, len);
}
});
}
unsafe fn rebuild_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) -> Box<[u8]> {
unsafe fn free_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) {
let cap = (offset as usize - buf as usize) + len;
Box::from_raw(slice::from_raw_parts_mut(buf, cap))
dealloc(buf, Layout::from_size_align(cap, 1).unwrap())
}
// ===== impl SharedVtable =====
struct Shared {
// holds vec for drop, but otherwise doesnt access it
_vec: Vec<u8>,
// Holds arguments to dealloc upon Drop, but otherwise doesn't use them
buf: *mut u8,
cap: usize,
ref_cnt: AtomicUsize,
}
impl Drop for Shared {
fn drop(&mut self) {
unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) }
}
}
// Assert that the alignment of `Shared` is divisible by 2.
// This is a necessary invariant since we depend on allocating `Shared` a
// shared object to implicitly carry the `KIND_ARC` flag in its pointer.
@@ -948,6 +1022,7 @@ const _: [(); 0 - mem::align_of::<Shared>() % 2] = []; // Assert that the alignm
static SHARED_VTABLE: Vtable = Vtable {
clone: shared_clone,
to_vec: shared_to_vec,
drop: shared_drop,
};
@@ -960,9 +1035,42 @@ unsafe fn shared_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Byte
shallow_clone_arc(shared as _, ptr, len)
}
unsafe fn shared_to_vec_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> Vec<u8> {
// Check that the ref_cnt is 1 (unique).
//
// If it is unique, then it is set to 0 with AcqRel fence for the same
// reason in release_shared.
//
// Otherwise, we take the other branch and call release_shared.
if (*shared)
.ref_cnt
.compare_exchange(1, 0, Ordering::AcqRel, Ordering::Relaxed)
.is_ok()
{
let buf = (*shared).buf;
let cap = (*shared).cap;
// Deallocate Shared
drop(Box::from_raw(shared as *mut mem::ManuallyDrop<Shared>));
// Copy back buffer
ptr::copy(ptr, buf, len);
Vec::from_raw_parts(buf, len, cap)
} else {
let v = slice::from_raw_parts(ptr, len).to_vec();
release_shared(shared);
v
}
}
unsafe fn shared_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
shared_to_vec_impl(data.load(Ordering::Relaxed).cast(), ptr, len)
}
unsafe fn shared_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
data.with_mut(|shared| {
release_shared(*shared as *mut Shared);
release_shared(shared.cast());
});
}
@@ -1000,9 +1108,9 @@ unsafe fn shallow_clone_vec(
// updated and since the buffer hasn't been promoted to an
// `Arc`, those three fields still are the components of the
// vector.
let vec = rebuild_boxed_slice(buf, offset, len).into_vec();
let shared = Box::new(Shared {
_vec: vec,
buf,
cap: (offset as usize - buf as usize) + len,
// Initialize refcount to 2. One for this reference, and one
// for the new clone that will be returned from
// `shallow_clone`.
@@ -1076,10 +1184,40 @@ unsafe fn release_shared(ptr: *mut Shared) {
// > "acquire" operation before deleting the object.
//
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
atomic::fence(Ordering::Acquire);
//
// Thread sanitizer does not support atomic fences. Use an atomic load
// instead.
(*ptr).ref_cnt.load(Ordering::Acquire);
// Drop the data
Box::from_raw(ptr);
drop(Box::from_raw(ptr));
}
// Ideally we would always use this version of `ptr_map` since it is strict
// provenance compatible, but it results in worse codegen. We will however still
// use it on miri because it gives better diagnostics for people who test bytes
// code with miri.
//
// See https://github.com/tokio-rs/bytes/pull/545 for more info.
#[cfg(miri)]
fn ptr_map<F>(ptr: *mut u8, f: F) -> *mut u8
where
F: FnOnce(usize) -> usize,
{
let old_addr = ptr as usize;
let new_addr = f(old_addr);
let diff = new_addr.wrapping_sub(old_addr);
ptr.wrapping_add(diff)
}
#[cfg(not(miri))]
fn ptr_map<F>(ptr: *mut u8, f: F) -> *mut u8
where
F: FnOnce(usize) -> usize,
{
let old_addr = ptr as usize;
let new_addr = f(old_addr);
new_addr as *mut u8
}
// compile-fails
+242 -54
View File
@@ -8,6 +8,7 @@ use alloc::{
borrow::{Borrow, BorrowMut},
boxed::Box,
string::String,
vec,
vec::Vec,
};
@@ -15,7 +16,7 @@ use crate::buf::{IntoIter, UninitSlice};
use crate::bytes::Vtable;
#[allow(unused)]
use crate::loom::sync::atomic::AtomicMut;
use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::{Buf, BufMut, Bytes};
/// A unique reference to a contiguous slice of memory.
@@ -252,12 +253,28 @@ impl BytesMut {
let ptr = self.ptr.as_ptr();
let len = self.len;
let data = AtomicPtr::new(self.data as _);
let data = AtomicPtr::new(self.data.cast());
mem::forget(self);
unsafe { Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE) }
}
}
/// Creates a new `BytesMut`, which is initialized with zero.
///
/// # Examples
///
/// ```
/// use bytes::BytesMut;
///
/// let zeros = BytesMut::zeroed(42);
///
/// assert_eq!(zeros.len(), 42);
/// zeros.into_iter().for_each(|x| assert_eq!(x, 0));
/// ```
pub fn zeroed(len: usize) -> BytesMut {
BytesMut::from_vec(vec![0; len])
}
/// Splits the bytes into two at the given index.
///
/// Afterwards `self` contains elements `[0, at)`, and the returned
@@ -380,6 +397,8 @@ impl BytesMut {
/// If `len` is greater than the buffer's current length, this has no
/// effect.
///
/// Existing underlying capacity is preserved.
///
/// The [`split_off`] method can emulate `truncate`, but this causes the
/// excess bytes to be returned instead of dropped.
///
@@ -402,7 +421,7 @@ impl BytesMut {
}
}
/// Clears the buffer, removing all data.
/// Clears the buffer, removing all data. Existing capacity is preserved.
///
/// # Examples
///
@@ -492,11 +511,20 @@ impl BytesMut {
/// reallocations. A call to `reserve` may result in an allocation.
///
/// Before allocating new buffer space, the function will attempt to reclaim
/// space in the existing buffer. If the current handle references a small
/// view in the original buffer and all other handles have been dropped,
/// and the requested capacity is less than or equal to the existing
/// buffer's capacity, then the current view will be copied to the front of
/// the buffer and the handle will take ownership of the full buffer.
/// space in the existing buffer. If the current handle references a view
/// into a larger original buffer, and all other handles referencing part
/// of the same original buffer have been dropped, then the current view
/// can be copied/shifted to the front of the buffer and the handle can take
/// ownership of the full buffer, provided that the full buffer is large
/// enough to fit the requested additional capacity.
///
/// This optimization will only happen if shifting the data from the current
/// view to the front of the buffer is not too expensive in terms of the
/// (amortized) time required. The precise condition is subject to change;
/// as of now, the length of the data being shifted needs to be at least as
/// large as the distance that it's shifted by. If the current view is empty
/// and the original buffer is large enough to fit the requested additional
/// capacity, then reallocations will never happen.
///
/// # Examples
///
@@ -560,17 +588,34 @@ impl BytesMut {
// space.
//
// Otherwise, since backed by a vector, use `Vec::reserve`
//
// We need to make sure that this optimization does not kill the
// amortized runtimes of BytesMut's operations.
unsafe {
let (off, prev) = self.get_vec_pos();
// Only reuse space if we can satisfy the requested additional space.
if self.capacity() - self.len() + off >= additional {
// There's space - reuse it
//
// Also check if the value of `off` suggests that enough bytes
// have been read to account for the overhead of shifting all
// the data (in an amortized analysis).
// Hence the condition `off >= self.len()`.
//
// This condition also already implies that the buffer is going
// to be (at least) half-empty in the end; so we do not break
// the (amortized) runtime with future resizes of the underlying
// `Vec`.
//
// [For more details check issue #524, and PR #525.]
if self.capacity() - self.len() + off >= additional && off >= self.len() {
// There's enough space, and it's not too much overhead:
// reuse the space!
//
// Just move the pointer back to the start after copying
// data back.
let base_ptr = self.ptr.as_ptr().offset(-(off as isize));
ptr::copy(self.ptr.as_ptr(), base_ptr, self.len);
// Since `off >= self.len()`, the two regions don't overlap.
ptr::copy_nonoverlapping(self.ptr.as_ptr(), base_ptr, self.len);
self.ptr = vptr(base_ptr);
self.set_vec_pos(0, prev);
@@ -578,13 +623,14 @@ impl BytesMut {
// can gain capacity back.
self.cap += off;
} else {
// No space - allocate more
// Not enough space, or reusing might be too much overhead:
// allocate more space!
let mut v =
ManuallyDrop::new(rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off));
v.reserve(additional);
// Update the info
self.ptr = vptr(v.as_mut_ptr().offset(off as isize));
self.ptr = vptr(v.as_mut_ptr().add(off));
self.len = v.len() - off;
self.cap = v.capacity() - off;
}
@@ -594,7 +640,7 @@ impl BytesMut {
}
debug_assert_eq!(kind, KIND_ARC);
let shared: *mut Shared = self.data as _;
let shared: *mut Shared = self.data;
// Reserving involves abandoning the currently shared buffer and
// allocating a new vector with the requested capacity.
@@ -617,29 +663,53 @@ impl BytesMut {
// sure that the vector has enough capacity.
let v = &mut (*shared).vec;
if v.capacity() >= new_cap {
// The capacity is sufficient, reclaim the buffer
let ptr = v.as_mut_ptr();
let v_capacity = v.capacity();
let ptr = v.as_mut_ptr();
ptr::copy(self.ptr.as_ptr(), ptr, len);
let offset = offset_from(self.ptr.as_ptr(), ptr);
// Compare the condition in the `kind == KIND_VEC` case above
// for more details.
if v_capacity >= new_cap && offset >= len {
// The capacity is sufficient, and copying is not too much
// overhead: reclaim the buffer!
// `offset >= len` means: no overlap
ptr::copy_nonoverlapping(self.ptr.as_ptr(), ptr, len);
self.ptr = vptr(ptr);
self.cap = v.capacity();
} else {
// calculate offset
let off = (self.ptr.as_ptr() as usize) - (v.as_ptr() as usize);
return;
// new_cap is calculated in terms of `BytesMut`, not the underlying
// `Vec`, so it does not take the offset into account.
//
// Thus we have to manually add it here.
new_cap = new_cap.checked_add(off).expect("overflow");
// The vector capacity is not sufficient. The reserve request is
// asking for more than the initial buffer capacity. Allocate more
// than requested if `new_cap` is not much bigger than the current
// capacity.
//
// There are some situations, using `reserve_exact` that the
// buffer capacity could be below `original_capacity`, so do a
// check.
let double = v.capacity().checked_shl(1).unwrap_or(new_cap);
new_cap = cmp::max(double, new_cap);
// No space - allocate more
v.reserve(new_cap - v.len());
// Update the info
self.ptr = vptr(v.as_mut_ptr().add(off));
self.cap = v.capacity() - off;
}
// The vector capacity is not sufficient. The reserve request is
// asking for more than the initial buffer capacity. Allocate more
// than requested if `new_cap` is not much bigger than the current
// capacity.
//
// There are some situations, using `reserve_exact` that the
// buffer capacity could be below `original_capacity`, so do a
// check.
let double = v.capacity().checked_shl(1).unwrap_or(new_cap);
new_cap = cmp::max(cmp::max(double, new_cap), original_capacity);
return;
} else {
new_cap = cmp::max(new_cap, original_capacity);
}
@@ -657,7 +727,7 @@ impl BytesMut {
// Update self
let data = (original_capacity_repr << ORIGINAL_CAPACITY_OFFSET) | KIND_VEC;
self.data = data as _;
self.data = invalid_ptr(data);
self.ptr = vptr(v.as_mut_ptr());
self.len = v.len();
self.cap = v.capacity();
@@ -688,7 +758,7 @@ impl BytesMut {
// Reserved above
debug_assert!(dst.len() >= cnt);
ptr::copy_nonoverlapping(extend.as_ptr(), dst.as_mut_ptr() as *mut u8, cnt);
ptr::copy_nonoverlapping(extend.as_ptr(), dst.as_mut_ptr(), cnt);
}
unsafe {
@@ -698,10 +768,11 @@ impl BytesMut {
/// Absorbs a `BytesMut` that was previously split off.
///
/// If the two `BytesMut` objects were previously contiguous, i.e., if
/// `other` was created by calling `split_off` on this `BytesMut`, then
/// this is an `O(1)` operation that just decreases a reference
/// count and sets a few indices. Otherwise this method degenerates to
/// If the two `BytesMut` objects were previously contiguous and not mutated
/// in a way that causes re-allocation i.e., if `other` was created by
/// calling `split_off` on this `BytesMut`, then this is an `O(1)` operation
/// that just decreases a reference count and sets a few indices.
/// Otherwise this method degenerates to
/// `self.extend_from_slice(other.as_ref())`.
///
/// # Examples
@@ -752,7 +823,7 @@ impl BytesMut {
ptr,
len,
cap,
data: data as *mut _,
data: invalid_ptr(data),
}
}
@@ -799,7 +870,7 @@ impl BytesMut {
// Updating the start of the view is setting `ptr` to point to the
// new start and updating the `len` field to reflect the new length
// of the view.
self.ptr = vptr(self.ptr.as_ptr().offset(start as isize));
self.ptr = vptr(self.ptr.as_ptr().add(start));
if self.len >= start {
self.len -= start;
@@ -819,11 +890,11 @@ impl BytesMut {
}
fn try_unsplit(&mut self, other: BytesMut) -> Result<(), BytesMut> {
if other.is_empty() {
if other.capacity() == 0 {
return Ok(());
}
let ptr = unsafe { self.ptr.as_ptr().offset(self.len as isize) };
let ptr = unsafe { self.ptr.as_ptr().add(self.len) };
if ptr == other.ptr.as_ptr()
&& self.kind() == KIND_ARC
&& other.kind() == KIND_ARC
@@ -873,7 +944,7 @@ impl BytesMut {
// always succeed.
debug_assert_eq!(shared as usize & KIND_MASK, KIND_ARC);
self.data = shared as _;
self.data = shared;
}
/// Makes an exact shallow clone of `self`.
@@ -906,13 +977,13 @@ impl BytesMut {
debug_assert_eq!(self.kind(), KIND_VEC);
debug_assert!(pos <= MAX_VEC_POS);
self.data = ((pos << VEC_POS_OFFSET) | (prev & NOT_VEC_POS_MASK)) as *mut _;
self.data = invalid_ptr((pos << VEC_POS_OFFSET) | (prev & NOT_VEC_POS_MASK));
}
#[inline]
fn uninit_slice(&mut self) -> &mut UninitSlice {
unsafe {
let ptr = self.ptr.as_ptr().offset(self.len as isize);
let ptr = self.ptr.as_ptr().add(self.len);
let len = self.cap - self.len;
UninitSlice::from_raw_parts_mut(ptr, len)
@@ -932,7 +1003,7 @@ impl Drop for BytesMut {
let _ = rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off);
}
} else if kind == KIND_ARC {
unsafe { release_shared(self.data as _) };
unsafe { release_shared(self.data) };
}
}
}
@@ -1010,6 +1081,19 @@ unsafe impl BufMut for BytesMut {
fn put_slice(&mut self, src: &[u8]) {
self.extend_from_slice(src);
}
fn put_bytes(&mut self, val: u8, cnt: usize) {
self.reserve(cnt);
unsafe {
let dst = self.uninit_slice();
// Reserved above
debug_assert!(dst.len() >= cnt);
ptr::write_bytes(dst.as_mut_ptr(), val, cnt);
self.advance_mut(cnt);
}
}
}
impl AsRef<[u8]> for BytesMut {
@@ -1146,7 +1230,7 @@ impl<'a> IntoIterator for &'a BytesMut {
type IntoIter = core::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.as_ref().into_iter()
self.as_ref().iter()
}
}
@@ -1175,7 +1259,18 @@ impl<'a> Extend<&'a u8> for BytesMut {
where
T: IntoIterator<Item = &'a u8>,
{
self.extend(iter.into_iter().map(|b| *b))
self.extend(iter.into_iter().copied())
}
}
impl Extend<Bytes> for BytesMut {
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = Bytes>,
{
for bytes in iter {
self.extend_from_slice(&bytes)
}
}
}
@@ -1187,7 +1282,7 @@ impl FromIterator<u8> for BytesMut {
impl<'a> FromIterator<&'a u8> for BytesMut {
fn from_iter<T: IntoIterator<Item = &'a u8>>(into_iter: T) -> Self {
BytesMut::from_iter(into_iter.into_iter().map(|b| *b))
BytesMut::from_iter(into_iter.into_iter().copied())
}
}
@@ -1228,10 +1323,13 @@ unsafe fn release_shared(ptr: *mut Shared) {
// > "acquire" operation before deleting the object.
//
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
atomic::fence(Ordering::Acquire);
//
// Thread sanitizer does not support atomic fences. Use an atomic load
// instead.
(*ptr).ref_count.load(Ordering::Acquire);
// Drop the data
Box::from_raw(ptr);
drop(Box::from_raw(ptr));
}
impl Shared {
@@ -1377,7 +1475,7 @@ impl PartialOrd<BytesMut> for str {
impl PartialEq<Vec<u8>> for BytesMut {
fn eq(&self, other: &Vec<u8>) -> bool {
*self == &other[..]
*self == other[..]
}
}
@@ -1401,7 +1499,7 @@ impl PartialOrd<BytesMut> for Vec<u8> {
impl PartialEq<String> for BytesMut {
fn eq(&self, other: &String) -> bool {
*self == &other[..]
*self == other[..]
}
}
@@ -1467,13 +1565,51 @@ impl PartialOrd<BytesMut> for &str {
impl PartialEq<BytesMut> for Bytes {
fn eq(&self, other: &BytesMut) -> bool {
&other[..] == &self[..]
other[..] == self[..]
}
}
impl PartialEq<Bytes> for BytesMut {
fn eq(&self, other: &Bytes) -> bool {
&other[..] == &self[..]
other[..] == self[..]
}
}
impl From<BytesMut> for Vec<u8> {
fn from(mut bytes: BytesMut) -> Self {
let kind = bytes.kind();
let mut vec = if kind == KIND_VEC {
unsafe {
let (off, _) = bytes.get_vec_pos();
rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off)
}
} else if kind == KIND_ARC {
let shared = bytes.data as *mut Shared;
if unsafe { (*shared).is_unique() } {
let vec = mem::replace(unsafe { &mut (*shared).vec }, Vec::new());
unsafe { release_shared(shared) };
vec
} else {
return bytes.deref().to_vec();
}
} else {
return bytes.deref().to_vec();
};
let len = bytes.len;
unsafe {
ptr::copy(bytes.ptr.as_ptr(), vec.as_mut_ptr(), len);
vec.set_len(len);
}
mem::forget(bytes);
vec
}
}
@@ -1486,6 +1622,35 @@ fn vptr(ptr: *mut u8) -> NonNull<u8> {
}
}
/// Returns a dangling pointer with the given address. This is used to store
/// integer data in pointer fields.
///
/// It is equivalent to `addr as *mut T`, but this fails on miri when strict
/// provenance checking is enabled.
#[inline]
fn invalid_ptr<T>(addr: usize) -> *mut T {
let ptr = core::ptr::null_mut::<u8>().wrapping_add(addr);
debug_assert_eq!(ptr as usize, addr);
ptr.cast::<T>()
}
/// Precondition: dst >= original
///
/// The following line is equivalent to:
///
/// ```rust,ignore
/// self.ptr.as_ptr().offset_from(ptr) as usize;
/// ```
///
/// But due to min rust is 1.39 and it is only stablised
/// in 1.47, we cannot use it.
#[inline]
fn offset_from(dst: *mut u8, original: *mut u8) -> usize {
debug_assert!(dst >= original);
dst as usize - original as usize
}
unsafe fn rebuild_vec(ptr: *mut u8, mut len: usize, mut cap: usize, off: usize) -> Vec<u8> {
let ptr = ptr.offset(-(off as isize));
len += off;
@@ -1498,6 +1663,7 @@ unsafe fn rebuild_vec(ptr: *mut u8, mut len: usize, mut cap: usize, off: usize)
static SHARED_VTABLE: Vtable = Vtable {
clone: shared_v_clone,
to_vec: shared_v_to_vec,
drop: shared_v_drop,
};
@@ -1505,10 +1671,32 @@ unsafe fn shared_v_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> By
let shared = data.load(Ordering::Relaxed) as *mut Shared;
increment_shared(shared);
let data = AtomicPtr::new(shared as _);
let data = AtomicPtr::new(shared as *mut ());
Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE)
}
unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
let shared: *mut Shared = data.load(Ordering::Relaxed).cast();
if (*shared).is_unique() {
let shared = &mut *shared;
// Drop shared
let mut vec = mem::replace(&mut shared.vec, Vec::new());
release_shared(shared);
// Copy back buffer
ptr::copy(ptr, vec.as_mut_ptr(), len);
vec.set_len(len);
vec
} else {
let v = slice::from_raw_parts(ptr, len).to_vec();
release_shared(shared);
v
}
}
unsafe fn shared_v_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
data.with_mut(|shared| {
release_shared(*shared as *mut Shared);
+3 -3
View File
@@ -25,7 +25,7 @@ impl Debug for BytesRef<'_> {
} else if b == b'\0' {
write!(f, "\\0")?;
// ASCII printable
} else if b >= 0x20 && b < 0x7f {
} else if (0x20..0x7f).contains(&b) {
write!(f, "{}", b as char)?;
} else {
write!(f, "\\x{:02x}", b)?;
@@ -38,12 +38,12 @@ impl Debug for BytesRef<'_> {
impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
Debug::fmt(&BytesRef(&self.as_ref()), f)
Debug::fmt(&BytesRef(self.as_ref()), f)
}
}
impl Debug for BytesMut {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
Debug::fmt(&BytesRef(&self.as_ref()), f)
Debug::fmt(&BytesRef(self.as_ref()), f)
}
}
-1
View File
@@ -3,7 +3,6 @@
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
#![doc(html_root_url = "https://docs.rs/bytes/1.0.1")]
#![no_std]
//! Provides abstractions for working with bytes.
+2 -2
View File
@@ -1,7 +1,7 @@
#[cfg(not(all(test, loom)))]
pub(crate) mod sync {
pub(crate) mod atomic {
pub(crate) use core::sync::atomic::{fence, AtomicPtr, AtomicUsize, Ordering};
pub(crate) use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
pub(crate) trait AtomicMut<T> {
fn with_mut<F, R>(&mut self, f: F) -> R
@@ -23,7 +23,7 @@ pub(crate) mod sync {
#[cfg(all(test, loom))]
pub(crate) mod sync {
pub(crate) mod atomic {
pub(crate) use loom::sync::atomic::{fence, AtomicPtr, AtomicUsize, Ordering};
pub(crate) use loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
pub(crate) trait AtomicMut<T> {}
}
+48 -2
View File
@@ -9,7 +9,7 @@ use core::usize;
fn test_vec_as_mut_buf() {
let mut buf = Vec::with_capacity(64);
assert_eq!(buf.remaining_mut(), usize::MAX);
assert_eq!(buf.remaining_mut(), isize::MAX as usize);
assert!(buf.chunk_mut().len() >= 64);
@@ -17,7 +17,7 @@ fn test_vec_as_mut_buf() {
assert_eq!(&buf, b"zomg");
assert_eq!(buf.remaining_mut(), usize::MAX - 4);
assert_eq!(buf.remaining_mut(), isize::MAX as usize - 4);
assert_eq!(buf.capacity(), 64);
for _ in 0..16 {
@@ -27,6 +27,14 @@ fn test_vec_as_mut_buf() {
assert_eq!(buf.len(), 68);
}
#[test]
fn test_vec_put_bytes() {
let mut buf = Vec::new();
buf.push(17);
buf.put_bytes(19, 2);
assert_eq!([17, 19, 19], &buf[..]);
}
#[test]
fn test_put_u8() {
let mut buf = Vec::with_capacity(8);
@@ -45,6 +53,34 @@ fn test_put_u16() {
assert_eq!(b"\x54\x21", &buf[..]);
}
#[test]
fn test_put_int() {
let mut buf = Vec::with_capacity(8);
buf.put_int(0x1020304050607080, 3);
assert_eq!(b"\x60\x70\x80", &buf[..]);
}
#[test]
#[should_panic]
fn test_put_int_nbytes_overflow() {
let mut buf = Vec::with_capacity(8);
buf.put_int(0x1020304050607080, 9);
}
#[test]
fn test_put_int_le() {
let mut buf = Vec::with_capacity(8);
buf.put_int_le(0x1020304050607080, 3);
assert_eq!(b"\x80\x70\x60", &buf[..]);
}
#[test]
#[should_panic]
fn test_put_int_le_nbytes_overflow() {
let mut buf = Vec::with_capacity(8);
buf.put_int_le(0x1020304050607080, 9);
}
#[test]
#[should_panic(expected = "cannot advance")]
fn test_vec_advance_mut() {
@@ -75,6 +111,16 @@ fn test_mut_slice() {
assert_eq!(&v, &[0, 0, 0, 42]);
}
#[test]
fn test_slice_put_bytes() {
let mut v = [0, 0, 0, 0];
let mut s = &mut v[..];
s.put_u8(17);
s.put_bytes(19, 2);
assert_eq!(1, s.remaining_mut());
assert_eq!(&[17, 19, 19, 0], &v[..]);
}
#[test]
fn test_deref_bufmut_forwards() {
struct Special;
+181 -7
View File
@@ -4,8 +4,8 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::usize;
const LONG: &'static [u8] = b"mary had a little lamb, little lamb, little lamb";
const SHORT: &'static [u8] = b"hello world";
const LONG: &[u8] = b"mary had a little lamb, little lamb, little lamb";
const SHORT: &[u8] = b"hello world";
fn is_sync<T: Sync>() {}
fn is_send<T: Send>() {}
@@ -411,8 +411,8 @@ fn freeze_after_split_off() {
fn fns_defined_for_bytes_mut() {
let mut bytes = BytesMut::from(&b"hello world"[..]);
bytes.as_ptr();
bytes.as_mut_ptr();
let _ = bytes.as_ptr();
let _ = bytes.as_mut_ptr();
// Iterator
let v: Vec<u8> = bytes.as_ref().iter().cloned().collect();
@@ -443,7 +443,7 @@ fn reserve_growth() {
let _ = bytes.split();
bytes.reserve(65);
assert_eq!(bytes.capacity(), 128);
assert_eq!(bytes.capacity(), 117);
}
#[test]
@@ -527,6 +527,25 @@ fn reserve_in_arc_nonunique_does_not_overallocate() {
assert_eq!(2001, bytes.capacity());
}
/// This function tests `BytesMut::reserve_inner`, where `BytesMut` holds
/// a unique reference to the shared vector and decide to reuse it
/// by reallocating the `Vec`.
#[test]
fn reserve_shared_reuse() {
let mut bytes = BytesMut::with_capacity(1000);
bytes.put_slice(b"Hello, World!");
drop(bytes.split());
bytes.put_slice(b"!123ex123,sadchELLO,_wORLD!");
// Use split_off so that v.capacity() - self.cap != off
drop(bytes.split_off(9));
assert_eq!(&*bytes, b"!123ex123");
bytes.reserve(2000);
assert_eq!(&*bytes, b"!123ex123");
assert_eq!(bytes.capacity(), 2009);
}
#[test]
fn extend_mut() {
let mut bytes = BytesMut::with_capacity(0);
@@ -544,6 +563,13 @@ fn extend_from_slice_mut() {
}
}
#[test]
fn extend_mut_from_bytes() {
let mut bytes = BytesMut::with_capacity(0);
bytes.extend([Bytes::from(LONG)]);
assert_eq!(*bytes, LONG[..]);
}
#[test]
fn extend_mut_without_size_hint() {
let mut bytes = BytesMut::with_capacity(0);
@@ -784,6 +810,31 @@ fn bytes_mut_unsplit_empty_self() {
assert_eq!(b"aaabbbcccddd", &buf[..]);
}
#[test]
fn bytes_mut_unsplit_other_keeps_capacity() {
let mut buf = BytesMut::with_capacity(64);
buf.extend_from_slice(b"aabb");
// non empty other created "from" buf
let mut other = buf.split_off(buf.len());
other.extend_from_slice(b"ccddee");
buf.unsplit(other);
assert_eq!(buf.capacity(), 64);
}
#[test]
fn bytes_mut_unsplit_empty_other_keeps_capacity() {
let mut buf = BytesMut::with_capacity(64);
buf.extend_from_slice(b"aabbccddee");
// empty other created "from" buf
let other = buf.split_off(buf.len());
buf.unsplit(other);
assert_eq!(buf.capacity(), 64);
}
#[test]
fn bytes_mut_unsplit_arc_different() {
let mut buf = BytesMut::with_capacity(64);
@@ -849,7 +900,7 @@ fn from_iter_no_size_hint() {
fn test_slice_ref(bytes: &Bytes, start: usize, end: usize, expected: &[u8]) {
let slice = &(bytes.as_ref()[start..end]);
let sub = bytes.slice_ref(&slice);
let sub = bytes.slice_ref(slice);
assert_eq!(&sub[..], expected);
}
@@ -869,7 +920,7 @@ fn slice_ref_empty() {
let bytes = Bytes::from(&b""[..]);
let slice = &(bytes.as_ref()[0..0]);
let sub = bytes.slice_ref(&slice);
let sub = bytes.slice_ref(slice);
assert_eq!(&sub[..], b"");
}
@@ -961,3 +1012,126 @@ fn bytes_with_capacity_but_empty() {
let vec = Vec::with_capacity(1);
let _ = Bytes::from(vec);
}
#[test]
fn bytes_put_bytes() {
let mut bytes = BytesMut::new();
bytes.put_u8(17);
bytes.put_bytes(19, 2);
assert_eq!([17, 19, 19], bytes.as_ref());
}
#[test]
fn box_slice_empty() {
// See https://github.com/tokio-rs/bytes/issues/340
let empty: Box<[u8]> = Default::default();
let b = Bytes::from(empty);
assert!(b.is_empty());
}
#[test]
fn bytes_into_vec() {
// Test kind == KIND_VEC
let content = b"helloworld";
let mut bytes = BytesMut::new();
bytes.put_slice(content);
let vec: Vec<u8> = bytes.into();
assert_eq!(&vec, content);
// Test kind == KIND_ARC, shared.is_unique() == True
let mut bytes = BytesMut::new();
bytes.put_slice(b"abcdewe23");
bytes.put_slice(content);
// Overwrite the bytes to make sure only one reference to the underlying
// Vec exists.
bytes = bytes.split_off(9);
let vec: Vec<u8> = bytes.into();
assert_eq!(&vec, content);
// Test kind == KIND_ARC, shared.is_unique() == False
let prefix = b"abcdewe23";
let mut bytes = BytesMut::new();
bytes.put_slice(prefix);
bytes.put_slice(content);
let vec: Vec<u8> = bytes.split_off(prefix.len()).into();
assert_eq!(&vec, content);
let vec: Vec<u8> = bytes.into();
assert_eq!(&vec, prefix);
}
#[test]
fn test_bytes_into_vec() {
// Test STATIC_VTABLE.to_vec
let bs = b"1b23exfcz3r";
let vec: Vec<u8> = Bytes::from_static(bs).into();
assert_eq!(&*vec, bs);
// Test bytes_mut.SHARED_VTABLE.to_vec impl
eprintln!("1");
let mut bytes_mut: BytesMut = bs[..].into();
// Set kind to KIND_ARC so that after freeze, Bytes will use bytes_mut.SHARED_VTABLE
eprintln!("2");
drop(bytes_mut.split_off(bs.len()));
eprintln!("3");
let b1 = bytes_mut.freeze();
eprintln!("4");
let b2 = b1.clone();
eprintln!("{:#?}", (&*b1).as_ptr());
// shared.is_unique() = False
eprintln!("5");
assert_eq!(&*Vec::from(b2), bs);
// shared.is_unique() = True
eprintln!("6");
assert_eq!(&*Vec::from(b1), bs);
// Test bytes_mut.SHARED_VTABLE.to_vec impl where offset != 0
let mut bytes_mut1: BytesMut = bs[..].into();
let bytes_mut2 = bytes_mut1.split_off(9);
let b1 = bytes_mut1.freeze();
let b2 = bytes_mut2.freeze();
assert_eq!(Vec::from(b2), bs[9..]);
assert_eq!(Vec::from(b1), bs[..9]);
}
#[test]
fn test_bytes_into_vec_promotable_even() {
let vec = vec![33u8; 1024];
// Test cases where kind == KIND_VEC
let b1 = Bytes::from(vec.clone());
assert_eq!(Vec::from(b1), vec);
// Test cases where kind == KIND_ARC, ref_cnt == 1
let b1 = Bytes::from(vec.clone());
drop(b1.clone());
assert_eq!(Vec::from(b1), vec);
// Test cases where kind == KIND_ARC, ref_cnt == 2
let b1 = Bytes::from(vec.clone());
let b2 = b1.clone();
assert_eq!(Vec::from(b1), vec);
// Test cases where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1
assert_eq!(Vec::from(b2), vec);
// Test cases where offset != 0
let mut b1 = Bytes::from(vec.clone());
let b2 = b1.split_off(20);
assert_eq!(Vec::from(b2), vec[20..]);
assert_eq!(Vec::from(b1), vec[..20]);
}
+30 -2
View File
@@ -24,8 +24,7 @@ unsafe impl GlobalAlloc for Odd {
};
let ptr = System.alloc(new_layout);
if !ptr.is_null() {
let ptr = ptr.offset(1);
ptr
ptr.offset(1)
} else {
ptr
}
@@ -67,3 +66,32 @@ fn test_bytes_clone_drop() {
let b1 = Bytes::from(vec);
let _b2 = b1.clone();
}
#[test]
fn test_bytes_into_vec() {
let vec = vec![33u8; 1024];
// Test cases where kind == KIND_VEC
let b1 = Bytes::from(vec.clone());
assert_eq!(Vec::from(b1), vec);
// Test cases where kind == KIND_ARC, ref_cnt == 1
let b1 = Bytes::from(vec.clone());
drop(b1.clone());
assert_eq!(Vec::from(b1), vec);
// Test cases where kind == KIND_ARC, ref_cnt == 2
let b1 = Bytes::from(vec.clone());
let b2 = b1.clone();
assert_eq!(Vec::from(b1), vec);
// Test cases where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1
assert_eq!(Vec::from(b2), vec);
// Test cases where offset != 0
let mut b1 = Bytes::from(vec.clone());
let b2 = b1.split_off(20);
assert_eq!(Vec::from(b2), vec[20..]);
assert_eq!(Vec::from(b1), vec[..20]);
}
+103 -39
View File
@@ -1,61 +1,87 @@
use std::alloc::{GlobalAlloc, Layout, System};
use std::{mem, ptr};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use bytes::{Buf, Bytes};
#[global_allocator]
static LEDGER: Ledger = Ledger;
static LEDGER: Ledger = Ledger::new();
struct Ledger;
const LEDGER_LENGTH: usize = 2048;
const USIZE_SIZE: usize = mem::size_of::<usize>();
struct Ledger {
alloc_table: [(AtomicPtr<u8>, AtomicUsize); LEDGER_LENGTH],
}
unsafe impl GlobalAlloc for Ledger {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() == 1 && layout.size() > 0 {
// Allocate extra space to stash a record of
// how much space there was.
let orig_size = layout.size();
let size = orig_size + USIZE_SIZE;
let new_layout = match Layout::from_size_align(size, 1) {
Ok(layout) => layout,
Err(_err) => return ptr::null_mut(),
};
let ptr = System.alloc(new_layout);
if !ptr.is_null() {
(ptr as *mut usize).write(orig_size);
let ptr = ptr.offset(USIZE_SIZE as isize);
ptr
} else {
ptr
impl Ledger {
const fn new() -> Self {
const ELEM: (AtomicPtr<u8>, AtomicUsize) =
(AtomicPtr::new(null_mut()), AtomicUsize::new(0));
let alloc_table = [ELEM; LEDGER_LENGTH];
Self { alloc_table }
}
/// Iterate over our table until we find an open entry, then insert into said entry
fn insert(&self, ptr: *mut u8, size: usize) {
for (entry_ptr, entry_size) in self.alloc_table.iter() {
// SeqCst is good enough here, we don't care about perf, i just want to be correct!
if entry_ptr
.compare_exchange(null_mut(), ptr, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
entry_size.store(size, Ordering::SeqCst);
break;
}
} else {
System.alloc(layout)
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if layout.align() == 1 && layout.size() > 0 {
let off_ptr = (ptr as *mut usize).offset(-1);
let orig_size = off_ptr.read();
if orig_size != layout.size() {
panic!(
"bad dealloc: alloc size was {}, dealloc size is {}",
orig_size,
layout.size()
);
fn remove(&self, ptr: *mut u8) -> usize {
for (entry_ptr, entry_size) in self.alloc_table.iter() {
// set the value to be something that will never try and be deallocated, so that we
// don't have any chance of a race condition
//
// dont worry, LEDGER_LENGTH is really long to compensate for us not reclaiming space
if entry_ptr
.compare_exchange(
ptr,
invalid_ptr(usize::MAX),
Ordering::SeqCst,
Ordering::SeqCst,
)
.is_ok()
{
return entry_size.load(Ordering::SeqCst);
}
}
let new_layout = match Layout::from_size_align(layout.size() + USIZE_SIZE, 1) {
Ok(layout) => layout,
Err(_err) => std::process::abort(),
};
System.dealloc(off_ptr as *mut u8, new_layout);
panic!("Couldn't find a matching entry for {:x?}", ptr);
}
}
unsafe impl GlobalAlloc for Ledger {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size();
let ptr = System.alloc(layout);
self.insert(ptr, size);
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let orig_size = self.remove(ptr);
if orig_size != layout.size() {
panic!(
"bad dealloc: alloc size was {}, dealloc size is {}",
orig_size,
layout.size()
);
} else {
System.dealloc(ptr, layout);
}
}
}
#[test]
fn test_bytes_advance() {
let mut bytes = Bytes::from(vec![10, 20, 30]);
@@ -77,3 +103,41 @@ fn test_bytes_truncate_and_advance() {
bytes.advance(1);
drop(bytes);
}
/// Returns a dangling pointer with the given address. This is used to store
/// integer data in pointer fields.
#[inline]
fn invalid_ptr<T>(addr: usize) -> *mut T {
let ptr = std::ptr::null_mut::<u8>().wrapping_add(addr);
debug_assert_eq!(ptr as usize, addr);
ptr.cast::<T>()
}
#[test]
fn test_bytes_into_vec() {
let vec = vec![33u8; 1024];
// Test cases where kind == KIND_VEC
let b1 = Bytes::from(vec.clone());
assert_eq!(Vec::from(b1), vec);
// Test cases where kind == KIND_ARC, ref_cnt == 1
let b1 = Bytes::from(vec.clone());
drop(b1.clone());
assert_eq!(Vec::from(b1), vec);
// Test cases where kind == KIND_ARC, ref_cnt == 2
let b1 = Bytes::from(vec.clone());
let b2 = b1.clone();
assert_eq!(Vec::from(b1), vec);
// Test cases where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1
assert_eq!(Vec::from(b2), vec);
// Test cases where offset != 0
let mut b1 = Bytes::from(vec.clone());
let b2 = b1.split_off(20);
assert_eq!(Vec::from(b2), vec[20..]);
assert_eq!(Vec::from(b1), vec[..20]);
}
+22
View File
@@ -133,6 +133,28 @@ fn vectored_read() {
}
}
#[test]
fn chain_growing_buffer() {
let mut buff = [' ' as u8; 10];
let mut vec = b"wassup".to_vec();
let mut chained = (&mut buff[..]).chain_mut(&mut vec).chain_mut(Vec::new()); // Required for potential overflow because remaining_mut for Vec is isize::MAX - vec.len(), but for chain_mut is usize::MAX
chained.put_slice(b"hey there123123");
assert_eq!(&buff, b"hey there1");
assert_eq!(&vec, b"wassup23123");
}
#[test]
fn chain_overflow_remaining_mut() {
let mut chained = Vec::<u8>::new().chain_mut(Vec::new()).chain_mut(Vec::new());
assert_eq!(chained.remaining_mut(), usize::MAX);
chained.put_slice(&[0; 256]);
assert_eq!(chained.remaining_mut(), usize::MAX);
}
#[test]
fn chain_get_bytes() {
let mut ab = Bytes::copy_from_slice(b"ab");