Compare commits

...
45 Commits
Author SHA1 Message Date
Alice RyhlandGitHub 76c0fbb54e Release bytes v1.12.1 (#838) 2026-07-08 12:00:46 +02:00
Alice RyhlandGitHub 924c82bf00 Handle unwinding from Box::new (#837) 2026-07-07 15:33:07 +02:00
Alice RyhlandGitHub 91402cee60 Release bytes v1.12.0 (#831) 2026-06-18 12:33:12 +02:00
vip892766gmaandGitHub 2256e6dc3e chore: add safety comments on unsafe blocks (#827) 2026-05-19 13:11:51 -04:00
DaniPopesandGitHub 245adff079 Pass vtable data by value (#826) 2026-04-29 07:28:59 +02:00
Mai Thanh MinhandGitHub 00cc5ff2bd Implement BytesMut::extend_from_within (#818) 2026-02-03 15:33:17 +01:00
Alice Ryhl 5b79d316c9 Merge tag 'v1.11.1' 2026-02-03 13:45:19 +00:00
Alice RyhlandGitHub 417dccdeff Release bytes v1.11.1 (#820) 2026-02-03 14:43:56 +01:00
Alice RyhlandGitHub d0293b0e35 Merge commit from fork
* Add repro for integer overflow

Signed-off-by: Alice Ryhl <[email protected]>

* Always check overflow in new_cap + offset

Signed-off-by: Alice Ryhl <[email protected]>

---------

Signed-off-by: Alice Ryhl <[email protected]>
2026-02-03 14:40:22 +01:00
Petros AngelatosandGitHub 804ee6d039 Make try_unsplit method public (#746) 2026-01-23 11:45:15 +01:00
Georg SemmlerandGitHub fd426ca084 Exclude development scripts from published package (#810)
During a dependency review we noticed that the bytes crate includes various development scripts. These development scripts shouldn't be there as they might, at some point become problematic. As of now they prevent any downstream user from enabling the `[bans.build.interpreted]` option of cargo deny.

I opted for using an explicit include list instead of an exclude list to prevent these files from being included in the published packages to make sure that everything that's included is an conscious choice.
2025-12-17 13:51:19 +00:00
Alice RyhlandGitHub b4ed70daee Add test for copy_to_bytes() -> BytesMut avoiding clone (#809) 2025-12-02 13:08:19 +01:00
Paolo BarboliniandGitHub 94e42915a9 Document that BytesMut::{reserve,try_reserve} doesn't preserve unused capacity (#808) 2025-11-28 10:02:34 +01:00
Paolo BarboliniandGitHub acd1e0ffb8 Fix get_int if nbytes is zero (#806) 2025-11-21 11:14:40 +01:00
Martin GrigorovandGitHub a7952fb447 chore: prepare bytes v1.11.0 (#804) 2025-11-14 15:44:53 +01:00
discord9andGitHub 60cbb776f2 fix: BytesMut only reuse if src has remaining (#803)
Signed-off-by: discord9 <[email protected]>
2025-11-14 10:52:50 +01:00
Alice RyhlandGitHub 7ce330f519 Move drop_fn of from_owner into vtable (#801) 2025-11-10 15:16:58 +01:00
Stepan KoltsovandGitHub 4b53a29eb2 Tweak BytesMut::remaining_mut (#795) 2025-08-20 11:57:49 +02:00
Stepan KoltsovandGitHub 016fdbdc7a Reserve capacity in BytesMut::put (#794) 2025-08-19 12:50:38 +02:00
Stepan KoltsovandGitHub ef7f25736c Specialize BytesMut::put::<Bytes> (#793) 2025-08-14 08:55:18 +00:00
Taiki EndoandGitHub 8b4f54d0f3 Ignore BytesMut::freeze doctest on wasm (#790) 2025-08-10 19:34:43 +09:00
Paolo BarboliniandGitHub 16132ad259 Fix latest clippy warnings (#787) 2025-07-16 10:53:50 +02:00
Paolo BarboliniandGitHub 3e44f88f5f Bump MSRV to 1.57 (#788) 2025-07-16 10:33:07 +02:00
Hange Shi - NJUandGitHub f29e93951d Add some tests for Limit, BytesMut and Reader (#785) 2025-05-28 12:56:41 +02:00
Michael FärberandGitHub d9d5c59ef8 Guarantee address in slice() for empty slices. (#780) 2025-05-20 14:52:05 +02:00
AbeZbm 5c90b11415 Add missing tests in test_bytes 2025-05-15 14:52:54 +08:00
Scott LambandGitHub 141cbc22cb Rename Vtable::to_* -> Vtable::into_* (#776) 2025-03-07 13:58:07 +00:00
Alice RyhlandGitHub 19d1427c97 chore: prepare bytes v1.10.1 (#774) 2025-03-05 13:11:49 +01:00
venomousmoogandGitHub 36675436cc Fix memory leak in owned_to_vec (#773) 2025-03-05 10:44:40 +01:00
Alice RyhlandGitHub e0f3a25fae chore: prepare bytes v1.10.0 (#766) 2025-02-03 12:49:49 +01:00
Taiki EndoandGitHub 16fd473d5c Add feature to support platforms without atomic CAS (#467) 2025-01-31 18:52:54 +09:00
Taiki EndoandGitHub 71824b095c ci: test AArch64 and run tests instead of build in cross job (#763) 2025-01-27 10:58:12 +01:00
Taiki EndoandGitHub 7a876609a2 Use [lints] to address unexpected_cfgs lint (#762) 2025-01-27 10:56:32 +01:00
hpenneandGitHub 3ab876fee6 Try get methods for Buf trait (#753) 2025-01-21 13:41:42 +01:00
Alice RyhlandGitHub a1b1208127 Disable test_bytes_vec_alloc test on miri (#755) 2025-01-13 16:28:28 +01:00
aae4969fde Add specialized Buf::chunks_vectored for Take (#617)
Co-authored-by: Alice Ryhl <[email protected]>
Co-authored-by: Michal 'vorner' Vaner <[email protected]>
2025-01-13 12:53:52 +00:00
Alice RyhlandGitHub 103d7bf9e0 Remove incorrect guarantee for chunks_vectored (#754) 2025-01-10 23:13:52 +01:00
Paolo BarboliniandGitHub 16cc574fea Implement Buf::chunks_vectored for VecDeque<u8> (#708) 2025-01-10 14:40:12 +01:00
Paolo BarboliniandGitHub 0e9e4fc2f1 Expand Buf tests (#747) 2025-01-09 17:13:47 +01:00
Alice RyhlandGitHub f7072c9267 Run tests under panic=abort (#749) 2024-12-10 22:30:27 +01:00
Kyle BarronandGitHub d0a14deeb5 chore: prepare bytes v1.9.0 (#748) 2024-11-28 13:43:45 +01:00
Leonid LogvinovandGitHub 54f1c26f69 Rename hex_impl! to fmt_impl! and reuse it for fmt::Debug (#743) 2024-11-08 10:23:02 +01:00
Paolo BarboliniandGitHub 4cd8969e85 Replace BufMut::put with BufMut::put_slice in Writer impl (#745) 2024-11-01 18:22:07 +00:00
katelyn martinandGitHub 2d996a2b41 Fix typo in Buf::chunk() comment (#744) 2024-11-01 14:46:09 +00:00
Adam CimarostiandGitHub 30ee8e9cba Add Bytes::from_owner (#742) 2024-10-29 13:18:03 +01:00
29 changed files with 3217 additions and 384 deletions
+46 -18
View File
@@ -63,6 +63,7 @@ jobs:
matrix:
os:
- ubuntu-latest
- ubuntu-22.04-arm # TODO: update to 24.04 when https://github.com/rust-lang/rust/issues/135867 solved
- macos-latest
- windows-latest
runs-on: ${{ matrix.os }}
@@ -89,34 +90,61 @@ jobs:
- name: Test
run: ci/test-stable.sh test
panic-abort:
name: panic=abort tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Run tests with -Cpanic=abort
run: ci/panic-abort.sh
# Run tests on some extra platforms
cross:
name: cross
strategy:
matrix:
target:
- i686-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf
- powerpc-unknown-linux-gnu
- powerpc64-unknown-linux-gnu
- wasm32-unknown-unknown
include:
- target: i686-unknown-linux-gnu
os: ubuntu-latest
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-22.04-arm # TODO: update to 24.04 when https://github.com/rust-lang/rust/issues/135867 solved
- target: powerpc-unknown-linux-gnu
os: ubuntu-latest
- target: powerpc64-unknown-linux-gnu
os: ubuntu-latest
- target: wasm32-wasip1
os: ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update stable
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
- name: Test
run: cargo test --target ${{ matrix.target }}
# Build for no_std environment.
no-std:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update stable
- name: Install cross
uses: taiki-e/install-action@cross
if: matrix.target != 'wasm32-unknown-unknown'
- name: cross build --target ${{ matrix.target }}
run: cross build --target ${{ matrix.target }}
if: matrix.target != 'wasm32-unknown-unknown'
# WASM support
- name: cargo build --target ${{ matrix.target }}
run: |
rustup target add ${{ matrix.target }}
cargo build --target ${{ matrix.target }}
if: matrix.target == 'wasm32-unknown-unknown'
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
# thumbv6m-none-eabi supports atomic, but not atomic CAS.
# thumbv7m-none-eabi supports atomic CAS.
- run: rustup target add thumbv6m-none-eabi thumbv7m-none-eabi
# * --optional-deps is needed for serde feature
# * --no-dev-deps is needed to avoid https://github.com/rust-lang/cargo/issues/4866
- run: cargo hack build --target thumbv7m-none-eabi --feature-powerset --skip std,default --optional-deps --no-dev-deps
# A sound way to provide atomic CAS on platforms without native atomic CAS is system-dependent.
# portable-atomic provides major ways via cfgs and accepts user-defined implementations via critical-section feature.
- run: cargo hack build --target thumbv6m-none-eabi --feature-powerset --skip std,default --optional-deps --no-dev-deps --features extra-platforms,extra-platforms/critical-section
# Sanitizers
tsan:
+80 -1
View File
@@ -1,3 +1,82 @@
# 1.12.1 (July 8th, 2026)
### Fixed
- Properly handle when `Box::new` panics (#837)
# 1.12.0 (June 18th, 2026)
### Added
- Add `BytesMut::extend_from_within()` (#818)
- Add `BytesMut::try_unsplit()` (#746)
### Fixed
- Fix panic in `get_int` if `nbytes` is zero (#806)
### Changed
- Pass vtable data by value (#826)
- Exclude development scripts from published package (#810)
### Documented
- Document that `BytesMut::{reserve,try_reserve}` doesn't preserve unused capacity (#808)
# 1.11.1 (February 3rd, 2026)
- Fix integer overflow in `BytesMut::reserve`
# 1.11.0 (November 14th, 2025)
- Bump MSRV to 1.57 (#788)
### Fixed
- fix: `BytesMut` only reuse if src has remaining (#803)
- Specialize `BytesMut::put::<Bytes>` (#793)
- Reserve capacity in `BytesMut::put` (#794)
- Change `BytesMut::remaining_mut` to use `isize::MAX` instead of `usize::MAX` (#795)
### Internal changes
- Guarantee address in `slice()` for empty slices. (#780)
- Rename `Vtable::to_*` -> `Vtable::into_*` (#776)
- Fix latest clippy warnings (#787)
- Ignore `BytesMut::freeze` doctest on wasm (#790)
- Move `drop_fn` of `from_owner` into vtable (#801)
# 1.10.1 (March 5th, 2025)
### Fixed
- Fix memory leak when using `to_vec` with `Bytes::from_owner` (#773)
# 1.10.0 (February 3rd, 2025)
### Added
- Add feature to support platforms without atomic CAS (#467)
- `try_get_*` methods for `Buf` trait (#753)
- Implement `Buf::chunks_vectored` for `Take` (#617)
- Implement `Buf::chunks_vectored` for `VecDeque<u8>` (#708)
### Fixed
- Remove incorrect guarantee for `chunks_vectored` (#754)
- Ensure that tests pass under `panic=abort` (#749)
# 1.9.0 (November 27, 2024)
### Added
- Add `Bytes::from_owner` to enable externally-allocated memory (#742)
### Documented
- Fix typo in Buf::chunk() comment (#744)
### Internal changes
- Replace BufMut::put with BufMut::put_slice in Writer impl (#745)
- Rename hex_impl! to fmt_impl! and reuse it for fmt::Debug (#743)
# 1.8.0 (October 21, 2024)
- Guarantee address in `split_off`/`split_to` for empty slices (#740)
@@ -14,7 +93,7 @@
### Internal changes
- Ensure BytesMut::advance reduces capacity (#728)
- Ensure BytesMut::advance reduces capacity (#728)
# 1.7.1 (August 1, 2024)
+14 -3
View File
@@ -4,9 +4,9 @@ name = "bytes"
# When releasing to crates.io:
# - Update CHANGELOG.md.
# - Create "v1.x.y" git tag.
version = "1.8.0"
edition = "2018"
rust-version = "1.39"
version = "1.12.1"
edition = "2021"
rust-version = "1.57"
license = "MIT"
authors = [
"Carl Lerche <[email protected]>",
@@ -17,6 +17,7 @@ repository = "https://github.com/tokio-rs/bytes"
readme = "README.md"
keywords = ["buffers", "zero-copy", "io"]
categories = ["network-programming", "data-structures"]
include = ["CHANGELOG.md", "LICENSE", "README.md", "SECURITY.md", "Cargo.toml", "src/**/*.rs", "tests/**/*.rs", "clippy.toml"]
[features]
default = ["std"]
@@ -24,6 +25,11 @@ std = []
[dependencies]
serde = { version = "1.0.60", optional = true, default-features = false, features = ["alloc"] }
# Use portable-atomic crate to support platforms without atomic CAS.
# See "no_std support" section in readme for more information.
#
# Enable require-cas feature to provide a better error message if the end user forgets to use the cfg or feature.
extra-platforms = { package = "portable-atomic", version = "1.3", optional = true, default-features = false, features = ["require-cas"] }
[dev-dependencies]
serde_test = "1.0"
@@ -33,3 +39,8 @@ loom = "0.7"
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(loom)',
] }
+17
View File
@@ -27,6 +27,21 @@ Next, add this to your crate:
use bytes::{Bytes, BytesMut, Buf, BufMut};
```
## no_std support
To use `bytes` with no_std environment, disable the (enabled by default) `std` feature.
```toml
[dependencies]
bytes = { version = "1", default-features = false }
```
To use `bytes` with no_std environment without atomic CAS, such as thumbv6m, you also need to enable
the `extra-platforms` feature. See the [documentation for the `portable-atomic`
crate](https://docs.rs/portable-atomic) for more information.
The MSRV when `extra-platforms` feature is enabled depends on the MSRV of `portable-atomic`.
## Serde support
Serde support is optional and disabled by default. To enable use the feature `serde`.
@@ -36,6 +51,8 @@ Serde support is optional and disabled by default. To enable use the feature `se
bytes = { version = "1", features = ["serde"] }
```
The MSRV when `serde` feature is enabled depends on the MSRV of `serde`.
## Building documentation
When building the `bytes` documentation the `docsrs` option should be used, otherwise
+3
View File
@@ -8,3 +8,6 @@ export MIRIFLAGS="-Zmiri-strict-provenance"
cargo miri test
cargo miri test --target mips64-unknown-linux-gnuabi64
# run with wrapping integer overflow instead of panic
cargo miri test --release
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
set -ex
RUSTFLAGS="$RUSTFLAGS -Cpanic=abort -Zpanic-abort-tests" cargo test --all-features --test '*'
+5 -2
View File
@@ -16,7 +16,10 @@ if [[ "${RUST_VERSION}" == "nightly"* ]]; then
cargo check --benches
# Check minimal versions
cargo clean
cargo update -Zminimal-versions
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
# from determining minimal versions based on dev-dependencies.
cargo hack --remove-dev-deps --workspace
# Update Cargo.lock to minimal version dependencies.
cargo update -Z minimal-versions
cargo check --all-features
fi
+1 -1
View File
@@ -1 +1 @@
msrv = "1.39"
msrv = "1.57"
+1534 -39
View File
File diff suppressed because it is too large Load Diff
+45 -15
View File
@@ -1,9 +1,9 @@
use crate::buf::{limit, Chain, Limit, UninitSlice};
#[cfg(feature = "std")]
use crate::buf::{writer, Writer};
use crate::{panic_advance, panic_does_not_fit};
use crate::{panic_advance, panic_does_not_fit, TryGetError};
use core::{mem, ptr, usize};
use core::{mem, ptr};
use alloc::{boxed::Box, vec::Vec};
@@ -204,7 +204,10 @@ pub unsafe trait BufMut {
Self: Sized,
{
if self.remaining_mut() < src.remaining() {
panic_advance(src.remaining(), self.remaining_mut());
panic_advance(&TryGetError {
requested: src.remaining(),
available: self.remaining_mut(),
});
}
while src.has_remaining() {
@@ -242,7 +245,10 @@ pub unsafe trait BufMut {
#[inline]
fn put_slice(&mut self, mut src: &[u8]) {
if self.remaining_mut() < src.len() {
panic_advance(src.len(), self.remaining_mut());
panic_advance(&TryGetError {
requested: src.len(),
available: self.remaining_mut(),
});
}
while !src.is_empty() {
@@ -285,7 +291,10 @@ pub unsafe trait BufMut {
#[inline]
fn put_bytes(&mut self, val: u8, mut cnt: usize) {
if self.remaining_mut() < cnt {
panic_advance(cnt, self.remaining_mut());
panic_advance(&TryGetError {
requested: cnt,
available: self.remaining_mut(),
})
}
while cnt > 0 {
@@ -1487,18 +1496,24 @@ unsafe impl BufMut for &mut [u8] {
#[inline]
unsafe fn advance_mut(&mut self, cnt: usize) {
if self.len() < cnt {
panic_advance(cnt, self.len());
panic_advance(&TryGetError {
requested: cnt,
available: self.len(),
});
}
// Lifetime dance taken from `impl Write for &mut [u8]`.
let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt);
let (_, b) = core::mem::take(self).split_at_mut(cnt);
*self = b;
}
#[inline]
fn put_slice(&mut self, src: &[u8]) {
if self.len() < src.len() {
panic_advance(src.len(), self.len());
panic_advance(&TryGetError {
requested: src.len(),
available: self.len(),
});
}
self[..src.len()].copy_from_slice(src);
@@ -1509,7 +1524,10 @@ unsafe impl BufMut for &mut [u8] {
#[inline]
fn put_bytes(&mut self, val: u8, cnt: usize) {
if self.len() < cnt {
panic_advance(cnt, self.len());
panic_advance(&TryGetError {
requested: cnt,
available: self.len(),
});
}
// SAFETY: We just checked that the pointer is valid for `cnt` bytes.
@@ -1534,18 +1552,24 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit<u8>] {
#[inline]
unsafe fn advance_mut(&mut self, cnt: usize) {
if self.len() < cnt {
panic_advance(cnt, self.len());
panic_advance(&TryGetError {
requested: cnt,
available: self.len(),
});
}
// Lifetime dance taken from `impl Write for &mut [u8]`.
let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt);
let (_, b) = core::mem::take(self).split_at_mut(cnt);
*self = b;
}
#[inline]
fn put_slice(&mut self, src: &[u8]) {
if self.len() < src.len() {
panic_advance(src.len(), self.len());
panic_advance(&TryGetError {
requested: src.len(),
available: self.len(),
});
}
// SAFETY: We just checked that the pointer is valid for `src.len()` bytes.
@@ -1558,7 +1582,10 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit<u8>] {
#[inline]
fn put_bytes(&mut self, val: u8, cnt: usize) {
if self.len() < cnt {
panic_advance(cnt, self.len());
panic_advance(&TryGetError {
requested: cnt,
available: self.len(),
});
}
// SAFETY: We just checked that the pointer is valid for `cnt` bytes.
@@ -1573,7 +1600,7 @@ unsafe impl BufMut for Vec<u8> {
#[inline]
fn remaining_mut(&self) -> usize {
// A vector can never have more than isize::MAX bytes
core::isize::MAX as usize - self.len()
isize::MAX as usize - self.len()
}
#[inline]
@@ -1582,7 +1609,10 @@ unsafe impl BufMut for Vec<u8> {
let remaining = self.capacity() - len;
if remaining < cnt {
panic_advance(cnt, remaining);
panic_advance(&TryGetError {
requested: cnt,
available: remaining,
});
}
// Addition will not overflow since the sum is at most the capacity.
+2 -2
View File
@@ -1,5 +1,5 @@
use crate::buf::{IntoIter, UninitSlice};
use crate::{Buf, BufMut, Bytes};
use crate::{Buf, BufMut};
#[cfg(feature = "std")]
use std::io::IoSlice;
@@ -169,7 +169,7 @@ where
n
}
fn copy_to_bytes(&mut self, len: usize) -> Bytes {
fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes {
let a_rem = self.a.remaining();
if a_rem >= len {
self.a.copy_to_bytes(len)
+34 -2
View File
@@ -1,7 +1,10 @@
use crate::{Buf, Bytes};
use crate::Buf;
use core::cmp;
#[cfg(feature = "std")]
use std::io::IoSlice;
/// A `Buf` adapter which limits the bytes read from an underlying buffer.
///
/// This struct is generally created by calling `take()` on `Buf`. See
@@ -145,11 +148,40 @@ impl<T: Buf> Buf for Take<T> {
self.limit -= cnt;
}
fn copy_to_bytes(&mut self, len: usize) -> Bytes {
fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes {
assert!(len <= self.remaining(), "`len` greater than remaining");
let r = self.inner.copy_to_bytes(len);
self.limit -= len;
r
}
#[cfg(feature = "std")]
fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
if self.limit == 0 {
return 0;
}
const LEN: usize = 16;
let mut slices: [IoSlice<'a>; LEN] = [IoSlice::new(&[]); LEN];
let cnt = self
.inner
.chunks_vectored(&mut slices[..dst.len().min(LEN)]);
let mut limit = self.limit;
for (i, (dst, slice)) in dst[..cnt].iter_mut().zip(slices.iter()).enumerate() {
if let Some(buf) = slice.get(..limit) {
// SAFETY: We could do this safely with `IoSlice::advance` if we had a larger MSRV.
let buf = unsafe { std::mem::transmute::<&[u8], &'a [u8]>(buf) };
*dst = IoSlice::new(buf);
return i + 1;
} else {
// SAFETY: We could do this safely with `IoSlice::advance` if we had a larger MSRV.
let buf = unsafe { std::mem::transmute::<&[u8], &'a [u8]>(slice) };
*dst = IoSlice::new(buf);
limit -= slice.len();
}
}
cnt
}
}
+18
View File
@@ -1,4 +1,6 @@
use alloc::collections::VecDeque;
#[cfg(feature = "std")]
use std::io;
use super::Buf;
@@ -16,6 +18,22 @@ impl Buf for VecDeque<u8> {
}
}
#[cfg(feature = "std")]
fn chunks_vectored<'a>(&'a self, dst: &mut [io::IoSlice<'a>]) -> usize {
if self.is_empty() || dst.is_empty() {
return 0;
}
let (s1, s2) = self.as_slices();
dst[0] = io::IoSlice::new(s1);
if s2.is_empty() || dst.len() == 1 {
return 1;
}
dst[1] = io::IoSlice::new(s2);
2
}
fn advance(&mut self, cnt: usize) {
self.drain(..cnt);
}
+1 -1
View File
@@ -78,7 +78,7 @@ impl<B: BufMut + Sized> io::Write for Writer<B> {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
let n = cmp::min(self.buf.remaining_mut(), src.len());
self.buf.put(&src[0..n]);
self.buf.put_slice(&src[..n]);
Ok(n)
}
+279 -127
View File
@@ -1,7 +1,7 @@
use core::iter::FromIterator;
use core::mem::{self, ManuallyDrop};
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::ops::{Deref, RangeBounds};
use core::{cmp, fmt, hash, ptr, slice, usize};
use core::ptr::NonNull;
use core::{cmp, fmt, hash, ptr, slice};
use alloc::{
alloc::{dealloc, Layout},
@@ -15,7 +15,7 @@ use crate::buf::IntoIter;
#[allow(unused)]
use crate::loom::sync::atomic::AtomicMut;
use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::{offset_from, Buf, BytesMut};
use crate::{Buf, BytesMut};
/// A cheaply cloneable and sliceable chunk of contiguous memory.
///
@@ -106,18 +106,22 @@ pub struct Bytes {
vtable: &'static Vtable,
}
// `data` is passed by value (`*mut ()` instead of `&mut AtomicPtr<()>`)
// when `&mut self` or `self` is consumed.
// This allows the optimizer to see that the address of the `Bytes` is not
// captured by the indirect call, enabling further optimizations.
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>,
pub to_mut: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> BytesMut,
/// `into_*` consumes the `Bytes`, returning the respective value.
pub into_vec: unsafe fn(*mut (), *const u8, usize) -> Vec<u8>,
pub into_mut: unsafe fn(*mut (), *const u8, usize) -> BytesMut,
/// fn(data)
pub is_unique: unsafe fn(&AtomicPtr<()>) -> bool,
/// fn(data, ptr, len)
pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize),
pub drop: unsafe fn(*mut (), *const u8, usize),
}
impl Bytes {
@@ -200,6 +204,91 @@ impl Bytes {
}
}
/// Create [Bytes] with a buffer whose lifetime is controlled
/// via an explicit owner.
///
/// A common use case is to zero-copy construct from mapped memory.
///
/// ```
/// # struct File;
/// #
/// # impl File {
/// # pub fn open(_: &str) -> Result<Self, ()> {
/// # Ok(Self)
/// # }
/// # }
/// #
/// # mod memmap2 {
/// # pub struct Mmap;
/// #
/// # impl Mmap {
/// # pub unsafe fn map(_file: &super::File) -> Result<Self, ()> {
/// # Ok(Self)
/// # }
/// # }
/// #
/// # impl AsRef<[u8]> for Mmap {
/// # fn as_ref(&self) -> &[u8] {
/// # b"buf"
/// # }
/// # }
/// # }
/// use bytes::Bytes;
/// use memmap2::Mmap;
///
/// # fn main() -> Result<(), ()> {
/// let file = File::open("upload_bundle.tar.gz")?;
/// let mmap = unsafe { Mmap::map(&file) }?;
/// let b = Bytes::from_owner(mmap);
/// # Ok(())
/// # }
/// ```
///
/// The `owner` will be transferred to the constructed [Bytes] object, which
/// will ensure it is dropped once all remaining clones of the constructed
/// object are dropped. The owner will then be responsible for dropping the
/// specified region of memory as part of its [Drop] implementation.
///
/// Note that converting [Bytes] constructed from an owner into a [BytesMut]
/// will always create a deep copy of the buffer into newly allocated memory.
pub fn from_owner<T>(owner: T) -> Self
where
T: AsRef<[u8]> + Send + 'static,
{
// Safety & Miri:
// The ownership of `owner` is first transferred to the `Owned` wrapper and `Bytes` object.
// This ensures that the owner is pinned in memory, allowing us to call `.as_ref()` safely
// since the lifetime of the owner is controlled by the lifetime of the new `Bytes` object,
// and the lifetime of the resulting borrowed `&[u8]` matches that of the owner.
// Note that this remains safe so long as we only call `.as_ref()` once.
//
// There are some additional special considerations here:
// * We rely on Bytes's Drop impl to clean up memory should `.as_ref()` panic.
// * Setting the `ptr` and `len` on the bytes object last (after moving the owner to
// Bytes) allows Miri checks to pass since it avoids obtaining the `&[u8]` slice
// from a stack-owned Box.
// More details on this: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813375863
// and: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813316032
let owned = Box::into_raw(Box::new(Owned {
ref_cnt: AtomicUsize::new(1),
owner,
}));
let mut ret = Bytes {
ptr: NonNull::dangling().as_ptr(),
len: 0,
data: AtomicPtr::new(owned.cast()),
vtable: &Owned::<T>::VTABLE,
};
let buf = unsafe { &*owned }.owner.as_ref();
ret.ptr = buf.as_ptr();
ret.len = buf.len();
ret
}
/// Returns the number of bytes contained in this `Bytes`.
///
/// # Examples
@@ -230,14 +319,16 @@ impl Bytes {
self.len == 0
}
/// Returns true if this is the only reference to the data.
/// Returns true if this is the only reference to the data and
/// `Into<BytesMut>` would avoid cloning the underlying buffer.
///
/// Always returns false if the data is backed by a static slice.
/// Always returns false if the data is backed by a [static slice](Bytes::from_static),
/// or an [owner](Bytes::from_owner).
///
/// The result of this method may be invalidated immediately if another
/// thread clones this value while this is being called. Ensure you have
/// unique access to this value (`&mut Bytes`) first if you need to be
/// certain the result is valid (i.e. for safety reasons)
/// certain the result is valid (i.e. for safety reasons).
/// # Examples
///
/// ```
@@ -280,37 +371,10 @@ impl Bytes {
/// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing
/// will panic.
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
use core::ops::Bound;
let len = self.len();
let begin = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
Bound::Excluded(&n) => n,
Bound::Unbounded => len,
};
assert!(
begin <= end,
"range start must not be greater than end: {:?} <= {:?}",
begin,
end,
);
assert!(
end <= len,
"range end out of bounds: {:?} <= {:?}",
end,
len,
);
let (begin, end) = crate::range(range, self.len());
if end == begin {
return Bytes::new();
return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(begin));
}
let mut ret = self.clone();
@@ -425,6 +489,8 @@ impl Bytes {
self.len = at;
// SAFETY: `at` has been asserted to be <= `self.len()`, and the
// `at == self.len()` and `at == 0` cases were handled above.
unsafe { ret.inc_start(at) };
ret
@@ -473,6 +539,8 @@ impl Bytes {
let mut ret = self.clone();
// SAFETY: `at` has been asserted to be <= `self.len()`, and the
// `at == self.len()` and `at == 0` cases were handled above.
unsafe { self.inc_start(at) };
ret.len = at;
@@ -536,6 +604,9 @@ impl Bytes {
/// If `self` is not unique for the entire original buffer, this will fail
/// and return self.
///
/// This will also always fail if the buffer was constructed via either
/// [from_owner](Bytes::from_owner) or [from_static](Bytes::from_static).
///
/// # Examples
///
/// ```
@@ -581,6 +652,11 @@ impl Bytes {
self.len -= by;
self.ptr = self.ptr.add(by);
}
#[inline]
fn data_mut(&mut self) -> *mut () {
self.data.with_mut(|p| *p)
}
}
// Vtable must enforce this behavior
@@ -590,7 +666,8 @@ unsafe impl Sync for Bytes {}
impl Drop for Bytes {
#[inline]
fn drop(&mut self) {
unsafe { (self.vtable.drop)(&mut self.data, self.ptr, self.len) }
let data = self.data_mut();
unsafe { (self.vtable.drop)(data, self.ptr, self.len) }
}
}
@@ -696,7 +773,7 @@ impl PartialEq for Bytes {
impl PartialOrd for Bytes {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
self.as_slice().partial_cmp(other.as_slice())
Some(self.cmp(other))
}
}
@@ -869,24 +946,26 @@ impl From<&'static str> for Bytes {
impl From<Vec<u8>> for Bytes {
fn from(vec: Vec<u8>) -> Bytes {
// Avoid an extra allocation if possible.
if vec.len() == vec.capacity() {
return Bytes::from(vec.into_boxed_slice());
}
let shared = Box::new(MaybeUninit::<Shared>::uninit());
let mut vec = ManuallyDrop::new(vec);
let ptr = vec.as_mut_ptr();
let len = vec.len();
let cap = vec.capacity();
// Avoid an extra allocation if possible.
if len == cap {
let vec = ManuallyDrop::into_inner(vec);
return Bytes::from(vec.into_boxed_slice());
}
let shared = Shared::init_to_raw(
shared,
Shared {
buf: ptr,
cap,
ref_cnt: AtomicUsize::new(1),
},
);
let shared = Box::new(Shared {
buf: ptr,
cap,
ref_cnt: AtomicUsize::new(1),
});
let shared = Box::into_raw(shared);
// The pointer should be aligned, so this assert should
// always succeed.
debug_assert!(
@@ -950,8 +1029,9 @@ impl From<Bytes> for BytesMut {
/// assert_eq!(BytesMut::from(bytes), BytesMut::from(&b"hello"[..]));
/// ```
fn from(bytes: Bytes) -> Self {
let bytes = ManuallyDrop::new(bytes);
unsafe { (bytes.vtable.to_mut)(&bytes.data, bytes.ptr, bytes.len) }
let mut bytes = ManuallyDrop::new(bytes);
let data = bytes.data_mut();
unsafe { (bytes.vtable.into_mut)(data, bytes.ptr, bytes.len) }
}
}
@@ -963,8 +1043,9 @@ impl From<String> for Bytes {
impl From<Bytes> for Vec<u8> {
fn from(bytes: Bytes) -> Vec<u8> {
let bytes = ManuallyDrop::new(bytes);
unsafe { (bytes.vtable.to_vec)(&bytes.data, bytes.ptr, bytes.len) }
let mut bytes = ManuallyDrop::new(bytes);
let data = bytes.data_mut();
unsafe { (bytes.vtable.into_vec)(data, bytes.ptr, bytes.len) }
}
}
@@ -983,8 +1064,8 @@ impl fmt::Debug for Vtable {
const STATIC_VTABLE: Vtable = Vtable {
clone: static_clone,
to_vec: static_to_vec,
to_mut: static_to_mut,
into_vec: static_to_vec,
into_mut: static_to_mut,
is_unique: static_is_unique,
drop: static_drop,
};
@@ -994,12 +1075,12 @@ 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> {
unsafe fn static_to_vec(_: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
let slice = slice::from_raw_parts(ptr, len);
slice.to_vec()
}
unsafe fn static_to_mut(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
unsafe fn static_to_mut(_: *mut (), ptr: *const u8, len: usize) -> BytesMut {
let slice = slice::from_raw_parts(ptr, len);
BytesMut::from(slice)
}
@@ -1008,24 +1089,94 @@ fn static_is_unique(_: &AtomicPtr<()>) -> bool {
false
}
unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
unsafe fn static_drop(_: *mut (), _: *const u8, _: usize) {
// nothing to drop for &'static [u8]
}
// ===== impl OwnedVtable =====
#[repr(C)]
struct Owned<T> {
ref_cnt: AtomicUsize,
owner: T,
}
impl<T> Owned<T> {
const VTABLE: Vtable = Vtable {
clone: owned_clone::<T>,
into_vec: owned_to_vec::<T>,
into_mut: owned_to_mut::<T>,
is_unique: owned_is_unique,
drop: owned_drop::<T>,
};
}
unsafe fn owned_clone<T>(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
let owned = data.load(Ordering::Relaxed);
let old_cnt = (*owned.cast::<AtomicUsize>()).fetch_add(1, Ordering::Relaxed);
if old_cnt > usize::MAX >> 1 {
crate::abort();
}
Bytes {
ptr,
len,
data: AtomicPtr::new(owned as _),
vtable: &Owned::<T>::VTABLE,
}
}
unsafe fn owned_to_vec<T>(owned: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
let slice = slice::from_raw_parts(ptr, len);
let vec = slice.to_vec();
owned_drop_impl::<T>(owned);
vec
}
unsafe fn owned_to_mut<T>(owned: *mut (), ptr: *const u8, len: usize) -> BytesMut {
BytesMut::from_vec(owned_to_vec::<T>(owned, ptr, len))
}
unsafe fn owned_is_unique(_data: &AtomicPtr<()>) -> bool {
false
}
unsafe fn owned_drop_impl<T>(owned: *mut ()) {
{
let ref_cnt = &*owned.cast::<AtomicUsize>();
let old_cnt = ref_cnt.fetch_sub(1, Ordering::Release);
debug_assert!(
old_cnt > 0 && old_cnt <= usize::MAX >> 1,
"expected non-zero refcount and no underflow"
);
if old_cnt != 1 {
return;
}
ref_cnt.load(Ordering::Acquire);
}
drop(Box::<Owned<T>>::from_raw(owned.cast()));
}
unsafe fn owned_drop<T>(data: *mut (), _ptr: *const u8, _len: usize) {
owned_drop_impl::<T>(data);
}
// ===== impl PromotableVtable =====
static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable {
clone: promotable_even_clone,
to_vec: promotable_even_to_vec,
to_mut: promotable_even_to_mut,
into_vec: promotable_even_to_vec,
into_mut: promotable_even_to_mut,
is_unique: promotable_is_unique,
drop: promotable_even_drop,
};
static PROMOTABLE_ODD_VTABLE: Vtable = Vtable {
clone: promotable_odd_clone,
to_vec: promotable_odd_to_vec,
to_mut: promotable_odd_to_mut,
into_vec: promotable_odd_to_vec,
into_mut: promotable_odd_to_mut,
is_unique: promotable_is_unique,
drop: promotable_odd_drop,
};
@@ -1044,12 +1195,11 @@ unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize
}
unsafe fn promotable_to_vec(
data: &AtomicPtr<()>,
shared: *mut (),
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 {
@@ -1060,7 +1210,7 @@ unsafe fn promotable_to_vec(
let buf = f(shared);
let cap = offset_from(ptr, buf) + len;
let cap = ptr.offset_from(buf) as usize + len;
// Copy back buffer
ptr::copy(ptr, buf, len);
@@ -1070,12 +1220,11 @@ unsafe fn promotable_to_vec(
}
unsafe fn promotable_to_mut(
data: &AtomicPtr<()>,
shared: *mut (),
ptr: *const u8,
len: usize,
f: fn(*mut ()) -> *mut u8,
) -> BytesMut {
let shared = data.load(Ordering::Acquire);
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
@@ -1088,7 +1237,7 @@ unsafe fn promotable_to_mut(
debug_assert_eq!(kind, KIND_VEC);
let buf = f(shared);
let off = offset_from(ptr, buf);
let off = ptr.offset_from(buf) as usize;
let cap = off + len;
let v = Vec::from_raw_parts(buf, cap, cap);
@@ -1098,31 +1247,28 @@ unsafe fn promotable_to_mut(
}
}
unsafe fn promotable_even_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
promotable_to_vec(data, ptr, len, |shared| {
unsafe fn promotable_even_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
promotable_to_vec(shared, ptr, len, |shared| {
ptr_map(shared.cast(), |addr| addr & !KIND_MASK)
})
}
unsafe fn promotable_even_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
promotable_to_mut(data, ptr, len, |shared| {
unsafe fn promotable_even_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
promotable_to_mut(shared, 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;
unsafe fn promotable_even_drop(shared: *mut (), ptr: *const u8, len: usize) {
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK);
free_boxed_slice(buf, ptr, len);
}
});
if kind == KIND_ARC {
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK);
free_boxed_slice(buf, ptr, len);
}
}
unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
@@ -1137,27 +1283,24 @@ unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize)
}
}
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_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
promotable_to_vec(shared, ptr, len, |shared| shared.cast())
}
unsafe fn promotable_odd_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
promotable_to_mut(data, ptr, len, |shared| shared.cast())
unsafe fn promotable_odd_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
promotable_to_mut(shared, 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;
unsafe fn promotable_odd_drop(shared: *mut (), ptr: *const u8, len: usize) {
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
if kind == KIND_ARC {
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
free_boxed_slice(shared.cast(), ptr, len);
}
});
free_boxed_slice(shared.cast(), ptr, len);
}
}
unsafe fn promotable_is_unique(data: &AtomicPtr<()>) -> bool {
@@ -1173,7 +1316,7 @@ unsafe fn promotable_is_unique(data: &AtomicPtr<()>) -> bool {
}
unsafe fn free_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) {
let cap = offset_from(offset, buf) + len;
let cap = offset.offset_from(buf) as usize + len;
dealloc(buf, Layout::from_size_align(cap, 1).unwrap())
}
@@ -1186,6 +1329,15 @@ struct Shared {
ref_cnt: AtomicUsize,
}
impl Shared {
fn init_to_raw(b: Box<MaybeUninit<Self>>, v: Self) -> *mut Self {
let shared = Box::into_raw(b).cast::<Self>();
// SAFETY: The Box has the right layout.
unsafe { shared.write(v) };
shared
}
}
impl Drop for Shared {
fn drop(&mut self) {
unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) }
@@ -1200,8 +1352,8 @@ const _: [(); 0 - mem::align_of::<Shared>() % 2] = []; // Assert that the alignm
static SHARED_VTABLE: Vtable = Vtable {
clone: shared_clone,
to_vec: shared_to_vec,
to_mut: shared_to_mut,
into_vec: shared_to_vec,
into_mut: shared_to_mut,
is_unique: shared_is_unique,
drop: shared_drop,
};
@@ -1244,8 +1396,8 @@ unsafe fn shared_to_vec_impl(shared: *mut Shared, ptr: *const u8, len: usize) ->
}
}
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_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
shared_to_vec_impl(shared.cast(), ptr, len)
}
unsafe fn shared_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> BytesMut {
@@ -1269,7 +1421,7 @@ unsafe fn shared_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) ->
let cap = shared.cap;
// Rebuild Vec
let off = offset_from(ptr, buf);
let off = ptr.offset_from(buf) as usize;
let v = Vec::from_raw_parts(buf, len + off, cap);
let mut b = BytesMut::from_vec(v);
@@ -1283,8 +1435,8 @@ unsafe fn shared_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) ->
}
}
unsafe fn shared_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
shared_to_mut_impl(data.load(Ordering::Relaxed).cast(), ptr, len)
unsafe fn shared_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
shared_to_mut_impl(shared.cast(), ptr, len)
}
pub(crate) unsafe fn shared_is_unique(data: &AtomicPtr<()>) -> bool {
@@ -1293,10 +1445,8 @@ pub(crate) unsafe fn shared_is_unique(data: &AtomicPtr<()>) -> bool {
ref_cnt == 1
}
unsafe fn shared_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
data.with_mut(|shared| {
release_shared(shared.cast());
});
unsafe fn shared_drop(shared: *mut (), _ptr: *const u8, _len: usize) {
release_shared(shared.cast());
}
unsafe fn shallow_clone_arc(shared: *mut Shared, ptr: *const u8, len: usize) -> Bytes {
@@ -1333,16 +1483,18 @@ 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 shared = Box::new(Shared {
buf,
cap: offset_from(offset, buf) + len,
// Initialize refcount to 2. One for this reference, and one
// for the new clone that will be returned from
// `shallow_clone`.
ref_cnt: AtomicUsize::new(2),
});
let shared = Box::into_raw(shared);
let shared = Box::new(MaybeUninit::<Shared>::uninit());
let shared = Shared::init_to_raw(
shared,
Shared {
buf,
cap: offset.offset_from(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`.
ref_cnt: AtomicUsize::new(2),
},
);
// The pointer should be aligned, so this assert should
// always succeed.
@@ -1362,7 +1514,7 @@ unsafe fn shallow_clone_vec(
// pointed to by `actual` will be visible.
match atom.compare_exchange(ptr as _, shared as _, Ordering::AcqRel, Ordering::Acquire) {
Ok(actual) => {
debug_assert!(actual as usize == ptr as usize);
debug_assert!(core::ptr::eq(actual, ptr));
// The upgrade was successful, the new handle can be
// returned.
Bytes {
+157 -46
View File
@@ -1,8 +1,7 @@
use core::iter::FromIterator;
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::ops::{Deref, DerefMut};
use core::ptr::{self, NonNull};
use core::{cmp, fmt, hash, isize, slice, usize};
use core::{cmp, fmt, hash, slice};
use alloc::{
borrow::{Borrow, BorrowMut},
@@ -17,7 +16,7 @@ use crate::bytes::Vtable;
#[allow(unused)]
use crate::loom::sync::atomic::AtomicMut;
use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::{offset_from, Buf, BufMut, Bytes};
use crate::{Buf, BufMut, Bytes, TryGetError};
/// A unique reference to a contiguous slice of memory.
///
@@ -80,6 +79,15 @@ struct Shared {
ref_count: AtomicUsize,
}
impl Shared {
fn init_to_raw(b: Box<MaybeUninit<Self>>, v: Self) -> *mut Self {
let shared = Box::into_raw(b).cast::<Self>();
// SAFETY: The Box has the right layout.
unsafe { shared.write(v) };
shared
}
}
// 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.
@@ -226,7 +234,7 @@ impl BytesMut {
///
/// # Examples
///
/// ```
/// ```ignore-wasm
/// use bytes::{BytesMut, BufMut};
/// use std::thread;
///
@@ -325,6 +333,10 @@ impl BytesMut {
self.capacity(),
);
unsafe {
// SAFETY: `shallow_clone` increments the reference count (or
// promotes to shared) and returns a bitwise copy of the handle.
// The caller immediately adjusts both handles so they represent
// disjoint regions.
let mut other = self.shallow_clone();
// SAFETY: We've checked that `at` <= `self.capacity()` above.
other.advance_unchecked(at);
@@ -401,6 +413,10 @@ impl BytesMut {
);
unsafe {
// SAFETY: `shallow_clone` increments the reference count (or
// promotes to shared) and returns a bitwise copy of the handle.
// The caller immediately adjusts both handles so they represent
// disjoint regions.
let mut other = self.shallow_clone();
// SAFETY: We've checked that `at` <= `self.len()` and we know that `self.len()` <=
// `self.capacity()`.
@@ -552,6 +568,8 @@ impl BytesMut {
/// and the original buffer is large enough to fit the requested additional
/// capacity, then reallocations will never happen.
///
/// This method does not preserve data stored in the unused capacity.
///
/// # Examples
///
/// In the following example, a new buffer is allocated.
@@ -694,11 +712,17 @@ impl BytesMut {
let v_capacity = v.capacity();
let ptr = v.as_mut_ptr();
let offset = offset_from(self.ptr.as_ptr(), ptr);
let offset = self.ptr.as_ptr().offset_from(ptr) as usize;
let new_cap_plus_offset = match new_cap.checked_add(offset) {
Some(new_cap_plus_offset) => new_cap_plus_offset,
None if !allocate => return false,
None => panic!("overflow"),
};
// Compare the condition in the `kind == KIND_VEC` case above
// for more details.
if v_capacity >= new_cap + offset {
if v_capacity >= new_cap_plus_offset {
self.cap = new_cap;
// no copy is necessary
} else if v_capacity >= new_cap && offset >= len {
@@ -714,14 +738,12 @@ impl BytesMut {
if !allocate {
return false;
}
// calculate offset
let off = (self.ptr.as_ptr() as usize) - (v.as_ptr() as usize);
// 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");
new_cap = new_cap_plus_offset;
// The vector capacity is not sufficient. The reserve request is
// asking for more than the initial buffer capacity. Allocate more
@@ -743,13 +765,13 @@ impl BytesMut {
// the unused capacity of the vector is copied over to the new
// allocation, so we need to ensure that we don't have any data we
// care about in the unused capacity before calling `reserve`.
debug_assert!(off + len <= v.capacity());
v.set_len(off + len);
debug_assert!(offset + len <= v.capacity());
v.set_len(offset + len);
v.reserve(new_cap - v.len());
// Update the info
self.ptr = vptr(v.as_mut_ptr().add(off));
self.cap = v.capacity() - off;
self.ptr = vptr(v.as_mut_ptr().add(offset));
self.cap = v.capacity() - offset;
}
return true;
@@ -780,7 +802,7 @@ impl BytesMut {
self.ptr = vptr(v.as_mut_ptr());
self.cap = v.capacity();
debug_assert_eq!(self.len, v.len());
return true;
true
}
/// Attempts to cheaply reclaim already allocated capacity for at least `additional` more
@@ -798,6 +820,8 @@ impl BytesMut {
/// references through other `BytesMut`s or `Bytes` which point to the same underlying
/// storage.
///
/// This method does not preserve data stored in the unused capacity.
///
/// # Examples
///
/// ```
@@ -877,7 +901,43 @@ impl BytesMut {
}
}
/// Absorbs a `BytesMut` that was previously split off.
/// Clones the elements in the given `range` within this `BytesMut` and
/// appends them to the end.
///
/// # Panics
///
/// Panics if `range` is out of bounds for this `BytesMut`.
///
/// # Examples
///
/// ```
/// use bytes::BytesMut;
///
/// let mut buf = BytesMut::with_capacity(0);
/// buf.extend_from_slice(b"aaabbb_");
/// buf.extend_from_within(3..6);
///
/// assert_eq!(b"aaabbb_bbb", &buf[..]);
/// ```
pub fn extend_from_within(&mut self, range: impl core::ops::RangeBounds<usize>) {
let (begin, end) = crate::range(range, self.len());
let cnt = end - begin;
self.reserve(cnt);
// SAFETY: range is already checked
let src = unsafe { self.as_ptr().add(begin) };
let dst = self.spare_capacity_mut();
// SAFETY: range doesn't overlap with spare capacity
unsafe { ptr::copy_nonoverlapping(src, dst.as_mut_ptr().cast(), cnt) }
// SAFETY: capacity is already reserved and filled with data
unsafe { self.advance_mut(cnt) }
}
/// Absorbs a `BytesMut` that was previously split off if they are
/// contiguous, otherwise appends its bytes to this `BytesMut`.
///
/// 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
@@ -986,11 +1046,39 @@ impl BytesMut {
// new start and updating the `len` field to reflect the new length
// of the view.
self.ptr = vptr(self.ptr.as_ptr().add(count));
self.len = self.len.checked_sub(count).unwrap_or(0);
self.len = self.len.saturating_sub(count);
self.cap -= count;
}
fn try_unsplit(&mut self, other: BytesMut) -> Result<(), 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 returns an error
/// containing the original `other`.
///
/// # Examples
///
/// ```
/// use bytes::BytesMut;
///
/// let mut buf = BytesMut::with_capacity(64);
/// buf.extend_from_slice(b"aaabbbcccddd");
///
/// let mut split_1 = buf.split_off(3);
/// let split_2 = split_1.split_off(3);
/// assert_eq!(b"aaa", &buf[..]);
/// assert_eq!(b"bbb", &split_1[..]);
/// assert_eq!(b"cccddd", &split_2[..]);
///
/// let split_2 = buf.try_unsplit(split_2).unwrap_err();
///
/// buf.try_unsplit(split_1).unwrap();
/// buf.try_unsplit(split_2).unwrap();
/// assert_eq!(b"aaabbbcccddd", &buf[..]);
/// ```
pub fn try_unsplit(&mut self, other: BytesMut) -> Result<(), BytesMut> {
if other.capacity() == 0 {
return Ok(());
}
@@ -1033,13 +1121,18 @@ impl BytesMut {
// updated and since the buffer hasn't been promoted to an
// `Arc`, those three fields still are the components of the
// vector.
let shared = Box::new(Shared {
vec: rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off),
original_capacity_repr,
ref_count: AtomicUsize::new(ref_cnt),
});
let shared = Box::into_raw(shared);
//
// Explicitly allocate before invoking rebuild_vec() so that
// the vector is not dropped if Box::new() panics.
let shared = Box::new(MaybeUninit::<Shared>::uninit());
let shared = Shared::init_to_raw(
shared,
Shared {
vec: rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off),
original_capacity_repr,
ref_count: AtomicUsize::new(ref_cnt),
},
);
// The pointer should be aligned, so this assert should
// always succeed.
@@ -1171,14 +1264,18 @@ impl Buf for BytesMut {
unsafe impl BufMut for BytesMut {
#[inline]
fn remaining_mut(&self) -> usize {
usize::MAX - self.len()
// Max allocation size is isize::MAX.
isize::MAX as usize - self.len()
}
#[inline]
unsafe fn advance_mut(&mut self, cnt: usize) {
let remaining = self.cap - self.len();
if cnt > remaining {
super::panic_advance(cnt, remaining);
super::panic_advance(&TryGetError {
requested: cnt,
available: remaining,
});
}
// Addition won't overflow since it is at most `self.cap`.
self.len = self.len() + cnt;
@@ -1199,11 +1296,27 @@ unsafe impl BufMut for BytesMut {
where
Self: Sized,
{
while src.has_remaining() {
let s = src.chunk();
let l = s.len();
self.extend_from_slice(s);
src.advance(l);
if !src.has_remaining() {
// prevent calling `copy_to_bytes`->`put`->`copy_to_bytes` infintely when src is empty
return;
} else if self.capacity() == 0 {
// When capacity is zero, try reusing allocation of `src`.
let src_copy = src.copy_to_bytes(src.remaining());
drop(src);
match src_copy.try_into_mut() {
Ok(bytes_mut) => *self = bytes_mut,
Err(bytes) => self.extend_from_slice(&bytes),
}
} else {
// In case the src isn't contiguous, reserve upfront.
self.reserve(src.remaining());
while src.has_remaining() {
let s = src.chunk();
let l = s.len();
self.extend_from_slice(s);
src.advance(l);
}
}
}
@@ -1281,7 +1394,7 @@ impl PartialEq for BytesMut {
impl PartialOrd for BytesMut {
fn partial_cmp(&self, other: &BytesMut) -> Option<cmp::Ordering> {
self.as_slice().partial_cmp(other.as_slice())
Some(self.cmp(other))
}
}
@@ -1716,10 +1829,10 @@ impl From<BytesMut> for Vec<u8> {
rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off)
}
} else {
let shared = bytes.data as *mut Shared;
let shared = bytes.data;
if unsafe { (*shared).is_unique() } {
let vec = mem::replace(unsafe { &mut (*shared).vec }, Vec::new());
let vec = core::mem::take(unsafe { &mut (*shared).vec });
unsafe { release_shared(shared) };
@@ -1773,8 +1886,8 @@ 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,
to_mut: shared_v_to_mut,
into_vec: shared_v_to_vec,
into_mut: shared_v_to_mut,
is_unique: shared_v_is_unique,
drop: shared_v_drop,
};
@@ -1787,14 +1900,14 @@ unsafe fn shared_v_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> By
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();
unsafe fn shared_v_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
let shared: *mut Shared = shared.cast();
if (*shared).is_unique() {
let shared = &mut *shared;
// Drop shared
let mut vec = mem::replace(&mut shared.vec, Vec::new());
let mut vec = core::mem::take(&mut shared.vec);
release_shared(shared);
// Copy back buffer
@@ -1809,8 +1922,8 @@ unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> V
}
}
unsafe fn shared_v_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
let shared: *mut Shared = data.load(Ordering::Relaxed).cast();
unsafe fn shared_v_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
let shared: *mut Shared = shared.cast();
if (*shared).is_unique() {
let shared = &mut *shared;
@@ -1820,7 +1933,7 @@ unsafe fn shared_v_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> B
let v = &mut shared.vec;
let v_capacity = v.capacity();
let v_ptr = v.as_mut_ptr();
let offset = offset_from(ptr as *mut u8, v_ptr);
let offset = ptr.offset_from(v_ptr) as usize;
let cap = v_capacity - offset;
let ptr = vptr(ptr as *mut u8);
@@ -1844,10 +1957,8 @@ unsafe fn shared_v_is_unique(data: &AtomicPtr<()>) -> bool {
ref_count == 1
}
unsafe fn shared_v_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
data.with_mut(|shared| {
release_shared(*shared as *mut Shared);
});
unsafe fn shared_v_drop(shared: *mut (), _ptr: *const u8, _len: usize) {
release_shared(shared.cast());
}
// compile-fails
+2 -11
View File
@@ -36,14 +36,5 @@ impl Debug for BytesRef<'_> {
}
}
impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
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)
}
}
fmt_impl!(Debug, Bytes);
fmt_impl!(Debug, BytesMut);
+4 -14
View File
@@ -21,17 +21,7 @@ impl UpperHex for BytesRef<'_> {
}
}
macro_rules! hex_impl {
($tr:ident, $ty:ty) => {
impl $tr for $ty {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
$tr::fmt(&BytesRef(self.as_ref()), f)
}
}
};
}
hex_impl!(LowerHex, Bytes);
hex_impl!(LowerHex, BytesMut);
hex_impl!(UpperHex, Bytes);
hex_impl!(UpperHex, BytesMut);
fmt_impl!(LowerHex, Bytes);
fmt_impl!(LowerHex, BytesMut);
fmt_impl!(UpperHex, Bytes);
fmt_impl!(UpperHex, BytesMut);
+10
View File
@@ -1,3 +1,13 @@
macro_rules! fmt_impl {
($tr:ident, $ty:ty) => {
impl $tr for $ty {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
$tr::fmt(&BytesRef(self.as_ref()), f)
}
}
};
}
mod debug;
mod hex;
+74 -20
View File
@@ -1,4 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![doc(test(
no_crate_inject,
@@ -115,7 +114,6 @@ fn abort() -> ! {
#[inline(always)]
#[cfg(feature = "std")]
fn saturating_sub_usize_u64(a: usize, b: u64) -> usize {
use core::convert::TryFrom;
match usize::try_from(b) {
Ok(b) => a.saturating_sub(b),
Err(_) => 0,
@@ -125,19 +123,90 @@ fn saturating_sub_usize_u64(a: usize, b: u64) -> usize {
#[inline(always)]
#[cfg(feature = "std")]
fn min_u64_usize(a: u64, b: usize) -> usize {
use core::convert::TryFrom;
match usize::try_from(a) {
Ok(a) => usize::min(a, b),
Err(_) => b,
}
}
/// Performs bounds checking of a range.
///
/// This is a spiritual copy of [core::slice::index::range] because that
/// function is currently unstable.
#[inline(always)]
#[track_caller]
fn range(range: impl core::ops::RangeBounds<usize>, len: usize) -> (usize, usize) {
use core::ops::Bound;
let begin = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
Bound::Excluded(&n) => n,
Bound::Unbounded => len,
};
assert!(
begin <= end,
"range start must not be greater than end: {:?} <= {:?}",
begin,
end,
);
assert!(
end <= len,
"range end out of bounds: {:?} <= {:?}",
end,
len,
);
(begin, end)
}
/// Error type for the `try_get_` methods of [`Buf`].
/// Indicates that there were not enough remaining
/// bytes in the buffer while attempting
/// to get a value from a [`Buf`] with one
/// of the `try_get_` methods.
#[derive(Debug, PartialEq, Eq)]
pub struct TryGetError {
/// The number of bytes necessary to get the value
pub requested: usize,
/// The number of bytes available in the buffer
pub available: usize,
}
impl core::fmt::Display for TryGetError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
f,
"Not enough bytes remaining in buffer to read value (requested {} but only {} available)",
self.requested,
self.available
)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryGetError {}
#[cfg(feature = "std")]
impl From<TryGetError> for std::io::Error {
fn from(error: TryGetError) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, error)
}
}
/// Panic with a nice error message.
#[cold]
fn panic_advance(idx: usize, len: usize) -> ! {
fn panic_advance(error_info: &TryGetError) -> ! {
panic!(
"advance out of bounds: the len is {} but advancing by {}",
len, idx
error_info.available, error_info.requested
);
}
@@ -148,18 +217,3 @@ fn panic_does_not_fit(size: usize, nbytes: usize) -> ! {
size, nbytes
);
}
/// 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 stabilized
/// in 1.47, we cannot use it.
#[inline]
fn offset_from(dst: *const u8, original: *const u8) -> usize {
dst as usize - original as usize
}
+3
View File
@@ -1,7 +1,10 @@
#[cfg(not(all(test, loom)))]
pub(crate) mod sync {
pub(crate) mod atomic {
#[cfg(not(feature = "extra-platforms"))]
pub(crate) use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
#[cfg(feature = "extra-platforms")]
pub(crate) use extra_platforms::{AtomicPtr, AtomicUsize, Ordering};
pub(crate) trait AtomicMut<T> {
fn with_mut<F, R>(&mut self, f: F) -> R
+403 -76
View File
@@ -1,88 +1,421 @@
#![warn(rust_2018_idioms)]
use bytes::Buf;
use ::bytes::{Buf, Bytes, BytesMut};
use core::{cmp, mem};
use std::collections::VecDeque;
#[cfg(feature = "std")]
use std::io::IoSlice;
#[test]
fn test_fresh_cursor_vec() {
let mut buf = &b"hello"[..];
// A random 64-byte ascii string, with the first 8 bytes altered to
// give valid representations of f32 and f64 (making them easier to compare)
// and negative signed numbers when interpreting as big endian
// (testing Sign Extension for `Buf::get_int' and `Buf::get_int_ne`).
const INPUT: &[u8] = b"\xffFqrjrDqPhvTc45vvq33f6bJrUtyHESuTeklWKgYd64xgzxJwvAkpYYnpNJyZSRn";
assert_eq!(buf.remaining(), 5);
assert_eq!(buf.chunk(), b"hello");
buf.advance(2);
assert_eq!(buf.remaining(), 3);
assert_eq!(buf.chunk(), b"llo");
buf.advance(3);
assert_eq!(buf.remaining(), 0);
assert_eq!(buf.chunk(), b"");
macro_rules! e {
($big_endian_val:expr, $little_endian_val:expr) => {
if cfg!(target_endian = "big") {
$big_endian_val
} else {
$little_endian_val
}
};
}
#[test]
fn test_get_u8() {
let mut buf = &b"\x21zomg"[..];
assert_eq!(0x21, buf.get_u8());
macro_rules! buf_tests {
($make_input:ident) => {
buf_tests!($make_input, true);
};
($make_input:ident, $checks_vectored_is_complete:expr) => {
use super::*;
#[test]
fn empty_state() {
let buf = $make_input(&[]);
assert_eq!(buf.remaining(), 0);
assert!(!buf.has_remaining());
assert!(buf.chunk().is_empty());
}
#[test]
fn fresh_state() {
let buf = $make_input(INPUT);
assert_eq!(buf.remaining(), 64);
assert!(buf.has_remaining());
let chunk = buf.chunk();
assert!(chunk.len() <= 64);
assert!(INPUT.starts_with(chunk));
}
#[test]
fn advance() {
let mut buf = $make_input(INPUT);
buf.advance(8);
assert_eq!(buf.remaining(), 64 - 8);
assert!(buf.has_remaining());
let chunk = buf.chunk();
assert!(chunk.len() <= 64 - 8);
assert!(INPUT[8..].starts_with(chunk));
}
#[test]
fn advance_to_end() {
let mut buf = $make_input(INPUT);
buf.advance(64);
assert_eq!(buf.remaining(), 0);
assert!(!buf.has_remaining());
let chunk = buf.chunk();
assert!(chunk.is_empty());
}
#[test]
#[should_panic]
fn advance_past_end() {
let mut buf = $make_input(INPUT);
buf.advance(65);
}
#[test]
#[cfg(feature = "std")]
fn chunks_vectored_empty() {
let buf = $make_input(&[]);
let mut bufs = [IoSlice::new(&[]); 16];
let n = buf.chunks_vectored(&mut bufs);
assert_eq!(n, 0);
assert!(bufs.iter().all(|buf| buf.is_empty()));
}
#[test]
#[cfg(feature = "std")]
fn chunks_vectored_is_complete() {
let buf = $make_input(INPUT);
let mut bufs = [IoSlice::new(&[]); 16];
let n = buf.chunks_vectored(&mut bufs);
assert!(n > 0);
assert!(n <= 16);
let bufs_concat = bufs[..n]
.iter()
.flat_map(|b| b.iter().copied())
.collect::<Vec<u8>>();
if $checks_vectored_is_complete {
assert_eq!(bufs_concat, INPUT);
} else {
// If this panics then `buf` implements `chunks_vectored`.
// Remove the `false` argument from `buf_tests!` for that type.
assert!(bufs_concat.len() < INPUT.len());
assert!(INPUT.starts_with(&bufs_concat));
}
for i in n..16 {
assert!(bufs[i].is_empty());
}
}
#[test]
fn copy_to_slice() {
let mut buf = $make_input(INPUT);
let mut chunk = [0u8; 8];
buf.copy_to_slice(&mut chunk);
assert_eq!(buf.remaining(), 64 - 8);
assert!(buf.has_remaining());
assert_eq!(chunk, INPUT[..8]);
let chunk = buf.chunk();
assert!(chunk.len() <= 64 - 8);
assert!(INPUT[8..].starts_with(chunk));
}
#[test]
fn copy_to_slice_big() {
let mut buf = $make_input(INPUT);
let mut chunk = [0u8; 56];
buf.copy_to_slice(&mut chunk);
assert_eq!(buf.remaining(), 64 - 56);
assert!(buf.has_remaining());
assert_eq!(chunk, INPUT[..56]);
let chunk = buf.chunk();
assert!(chunk.len() <= 64 - 56);
assert!(INPUT[56..].starts_with(chunk));
}
#[test]
fn copy_to_slice_to_end() {
let mut buf = $make_input(INPUT);
let mut chunk = [0u8; 64];
buf.copy_to_slice(&mut chunk);
assert_eq!(buf.remaining(), 0);
assert!(!buf.has_remaining());
assert_eq!(chunk, INPUT);
assert!(buf.chunk().is_empty());
}
#[test]
#[should_panic]
fn copy_to_slice_overflow() {
let mut buf = $make_input(INPUT);
let mut chunk = [0u8; 65];
buf.copy_to_slice(&mut chunk);
}
#[test]
fn copy_to_bytes() {
let mut buf = $make_input(INPUT);
let chunk = buf.copy_to_bytes(8);
assert_eq!(buf.remaining(), 64 - 8);
assert!(buf.has_remaining());
assert_eq!(chunk, INPUT[..8]);
let chunk = buf.chunk();
assert!(chunk.len() <= 64 - 8);
assert!(INPUT[8..].starts_with(chunk));
}
#[test]
fn copy_to_bytes_big() {
let mut buf = $make_input(INPUT);
let chunk = buf.copy_to_bytes(56);
assert_eq!(buf.remaining(), 64 - 56);
assert!(buf.has_remaining());
assert_eq!(chunk, INPUT[..56]);
let chunk = buf.chunk();
assert!(chunk.len() <= 64 - 56);
assert!(INPUT[56..].starts_with(chunk));
}
#[test]
fn copy_to_bytes_to_end() {
let mut buf = $make_input(INPUT);
let chunk = buf.copy_to_bytes(64);
assert_eq!(buf.remaining(), 0);
assert!(!buf.has_remaining());
assert_eq!(chunk, INPUT);
assert!(buf.chunk().is_empty());
}
#[test]
#[should_panic]
fn copy_to_bytes_overflow() {
let mut buf = $make_input(INPUT);
let _ = buf.copy_to_bytes(65);
}
buf_tests!(number $make_input, get_u8, get_u8_overflow, u8, get_u8, 0xff);
buf_tests!(number $make_input, get_i8, get_i8_overflow, i8, get_i8, 0xffu8 as i8);
buf_tests!(number $make_input, get_u16_be, get_u16_be_overflow, u16, get_u16, 0xff46);
buf_tests!(number $make_input, get_u16_le, get_u16_le_overflow, u16, get_u16_le, 0x46ff);
buf_tests!(number $make_input, get_u16_ne, get_u16_ne_overflow, u16, get_u16_ne, e!(0xff46, 0x46ff));
buf_tests!(number $make_input, get_i16_be, get_i16_be_overflow, i16, get_i16, 0xff46u16 as i16);
buf_tests!(number $make_input, get_i16_le, get_i16_le_overflow, i16, get_i16_le, 0x46ff);
buf_tests!(number $make_input, get_i16_ne, get_i16_ne_overflow, i16, get_i16_ne, e!(0xff46u16 as i16, 0x46ff));
buf_tests!(number $make_input, get_u32_be, get_u32_be_overflow, u32, get_u32, 0xff467172);
buf_tests!(number $make_input, get_u32_le, get_u32_le_overflow, u32, get_u32_le, 0x727146ff);
buf_tests!(number $make_input, get_u32_ne, get_u32_ne_overflow, u32, get_u32_ne, e!(0xff467172, 0x727146ff));
buf_tests!(number $make_input, get_i32_be, get_i32_be_overflow, i32, get_i32, 0xff467172u32 as i32);
buf_tests!(number $make_input, get_i32_le, get_i32_le_overflow, i32, get_i32_le, 0x727146ff);
buf_tests!(number $make_input, get_i32_ne, get_i32_ne_overflow, i32, get_i32_ne, e!(0xff467172u32 as i32, 0x727146ff));
buf_tests!(number $make_input, get_u64_be, get_u64_be_overflow, u64, get_u64, 0xff4671726a724471);
buf_tests!(number $make_input, get_u64_le, get_u64_le_overflow, u64, get_u64_le, 0x7144726a727146ff);
buf_tests!(number $make_input, get_u64_ne, get_u64_ne_overflow, u64, get_u64_ne, e!(0xff4671726a724471, 0x7144726a727146ff));
buf_tests!(number $make_input, get_i64_be, get_i64_be_overflow, i64, get_i64, 0xff4671726a724471u64 as i64);
buf_tests!(number $make_input, get_i64_le, get_i64_le_overflow, i64, get_i64_le, 0x7144726a727146ff);
buf_tests!(number $make_input, get_i64_ne, get_i64_ne_overflow, i64, get_i64_ne, e!(0xff4671726a724471u64 as i64, 0x7144726a727146ff));
buf_tests!(number $make_input, get_u128_be, get_u128_be_overflow, u128, get_u128, 0xff4671726a7244715068765463343576);
buf_tests!(number $make_input, get_u128_le, get_u128_le_overflow, u128, get_u128_le, 0x76353463547668507144726a727146ff);
buf_tests!(number $make_input, get_u128_ne, get_u128_ne_overflow, u128, get_u128_ne, e!(0xff4671726a7244715068765463343576, 0x76353463547668507144726a727146ff));
buf_tests!(number $make_input, get_i128_be, get_i128_be_overflow, i128, get_i128, 0xff4671726a7244715068765463343576u128 as i128);
buf_tests!(number $make_input, get_i128_le, get_i128_le_overflow, i128, get_i128_le, 0x76353463547668507144726a727146ff);
buf_tests!(number $make_input, get_i128_ne, get_i128_ne_overflow, i128, get_i128_ne, e!(0xff4671726a7244715068765463343576u128 as i128, 0x76353463547668507144726a727146ff));
buf_tests!(number $make_input, get_f32_be, get_f32_be_overflow, f32, get_f32, f32::from_bits(0xff467172));
buf_tests!(number $make_input, get_f32_le, get_f32_le_overflow, f32, get_f32_le, f32::from_bits(0x727146ff));
buf_tests!(number $make_input, get_f32_ne, get_f32_ne_overflow, f32, get_f32_ne, f32::from_bits(e!(0xff467172, 0x727146ff)));
buf_tests!(number $make_input, get_f64_be, get_f64_be_overflow, f64, get_f64, f64::from_bits(0xff4671726a724471));
buf_tests!(number $make_input, get_f64_le, get_f64_le_overflow, f64, get_f64_le, f64::from_bits(0x7144726a727146ff));
buf_tests!(number $make_input, get_f64_ne, get_f64_ne_overflow, f64, get_f64_ne, f64::from_bits(e!(0xff4671726a724471, 0x7144726a727146ff)));
buf_tests!(var_number $make_input, get_uint_be, get_uint_be_zero, get_uint_be_overflow, u64, get_uint, 3, 0xff4671);
buf_tests!(var_number $make_input, get_uint_le, get_uint_le_zero, get_uint_le_overflow, u64, get_uint_le, 3, 0x7146ff);
buf_tests!(var_number $make_input, get_uint_ne, get_uint_ne_zero, get_uint_ne_overflow, u64, get_uint_ne, 3, e!(0xff4671, 0x7146ff));
buf_tests!(var_number $make_input, get_int_be, get_int_be_zero, get_int_be_overflow, i64, get_int, 3, 0xffffffffffff4671u64 as i64);
buf_tests!(var_number $make_input, get_int_le, get_int_le_zero, get_int_le_overflow, i64, get_int_le, 3, 0x7146ff);
buf_tests!(var_number $make_input, get_int_ne, get_int_ne_zero, get_int_ne_overflow, i64, get_int_ne, 3, e!(0xffffffffffff4671u64 as i64, 0x7146ff));
};
(number $make_input:ident, $ok_name:ident, $panic_name:ident, $number:ty, $method:ident, $value:expr) => {
#[test]
fn $ok_name() {
let mut buf = $make_input(INPUT);
let value = buf.$method();
assert_eq!(buf.remaining(), 64 - mem::size_of::<$number>());
assert!(buf.has_remaining());
assert_eq!(value, $value);
}
#[test]
#[should_panic]
fn $panic_name() {
let mut buf = $make_input(&[]);
let _ = buf.$method();
}
};
(var_number $make_input:ident, $ok_name:ident, $ok_zero_name:ident, $panic_name:ident, $number:ty, $method:ident, $len:expr, $value:expr) => {
#[test]
fn $ok_name() {
let mut buf = $make_input(INPUT);
let value = buf.$method($len);
assert_eq!(buf.remaining(), 64 - $len);
assert!(buf.has_remaining());
assert_eq!(value, $value);
}
// Regression test for https://github.com/tokio-rs/bytes/issues/798
#[test]
fn $ok_zero_name() {
let mut buf = $make_input(INPUT);
let value = buf.$method(0);
assert_eq!(buf.remaining(), 64);
assert!(buf.has_remaining());
assert_eq!(value, 0);
}
#[test]
#[should_panic]
fn $panic_name() {
let mut buf = $make_input(&[]);
let _ = buf.$method($len);
}
};
}
#[test]
fn test_get_u16() {
let mut buf = &b"\x21\x54zomg"[..];
assert_eq!(0x2154, buf.get_u16());
let mut buf = &b"\x21\x54zomg"[..];
assert_eq!(0x5421, buf.get_u16_le());
mod u8_slice {
fn make_input(buf: &'static [u8]) -> &'static [u8] {
buf
}
buf_tests!(make_input);
}
#[test]
fn test_get_int() {
let mut buf = &b"\xd6zomg"[..];
assert_eq!(-42, buf.get_int(1));
let mut buf = &b"\xd6zomg"[..];
assert_eq!(-42, buf.get_int_le(1));
mod bytes {
fn make_input(buf: &'static [u8]) -> impl Buf {
Bytes::from_static(buf)
}
let mut buf = &b"\xfe\x1d\xc0zomg"[..];
assert_eq!(0xffffffffffc01dfeu64 as i64, buf.get_int_le(3));
let mut buf = &b"\xfe\x1d\xc0zomg"[..];
assert_eq!(0xfffffffffffe1dc0u64 as i64, buf.get_int(3));
buf_tests!(make_input);
}
#[test]
#[should_panic]
fn test_get_u16_buffer_underflow() {
let mut buf = &b"\x21"[..];
buf.get_u16();
mod bytes_mut {
fn make_input(buf: &'static [u8]) -> impl Buf {
BytesMut::from(buf)
}
buf_tests!(make_input);
}
mod vec_deque {
fn make_input(buf: &'static [u8]) -> impl Buf {
let mut deque = VecDeque::new();
if !buf.is_empty() {
// Construct |b|some bytes|a| `VecDeque`
let mid = buf.len() / 2;
let (a, b) = buf.split_at(mid);
deque.reserve_exact(buf.len() + 1);
let extra_space = deque.capacity() - b.len() - 1;
deque.resize(extra_space, 0);
deque.extend(a);
deque.drain(..extra_space);
deque.extend(b);
let (a, b) = deque.as_slices();
assert!(
!a.is_empty(),
"could not setup test - attempt to create discontiguous VecDeque failed"
);
assert!(
!b.is_empty(),
"could not setup test - attempt to create discontiguous VecDeque failed"
);
}
deque
}
buf_tests!(make_input, true);
}
#[cfg(feature = "std")]
#[test]
fn test_bufs_vec() {
let buf = &b"hello world"[..];
mod cursor {
use std::io::Cursor;
let b1: &[u8] = &mut [];
let b2: &[u8] = &mut [];
fn make_input(buf: &'static [u8]) -> impl Buf {
Cursor::new(buf)
}
let mut dst = [IoSlice::new(b1), IoSlice::new(b2)];
assert_eq!(1, buf.chunks_vectored(&mut dst[..]));
buf_tests!(make_input);
}
#[test]
fn test_vec_deque() {
use std::collections::VecDeque;
mod box_bytes {
fn make_input(buf: &'static [u8]) -> impl Buf {
Box::new(Bytes::from_static(buf))
}
let mut buffer: VecDeque<u8> = VecDeque::new();
buffer.extend(b"hello world");
assert_eq!(11, buffer.remaining());
assert_eq!(b"hello world", buffer.chunk());
buffer.advance(6);
assert_eq!(b"world", buffer.chunk());
buffer.extend(b" piece");
let mut out = [0; 11];
buffer.copy_to_slice(&mut out);
assert_eq!(b"world piece", &out[..]);
buf_tests!(make_input);
}
mod chain_u8_slice {
fn make_input(buf: &'static [u8]) -> impl Buf {
let (a, b) = buf.split_at(buf.len() / 2);
Buf::chain(a, b)
}
buf_tests!(make_input);
}
mod chain_small_big_u8_slice {
fn make_input(buf: &'static [u8]) -> impl Buf {
let mid = cmp::min(1, buf.len());
let (a, b) = buf.split_at(mid);
Buf::chain(a, b)
}
buf_tests!(make_input);
}
mod chain_limited_slices {
fn make_input(buf: &'static [u8]) -> impl Buf {
let buf3 = &buf[cmp::min(buf.len(), 3)..];
let a = Buf::take(buf3, 0);
let b = Buf::take(buf, 3);
let c = Buf::take(buf3, usize::MAX);
let d = buf;
Buf::take(Buf::chain(Buf::chain(a, b), Buf::chain(c, d)), buf.len())
}
buf_tests!(make_input, true);
}
#[allow(unused_allocation)] // This is intentional.
@@ -117,18 +450,12 @@ fn test_deref_buf_forwards() {
}
#[test]
fn copy_to_bytes_less() {
let mut buf = &b"hello world"[..];
let bytes = buf.copy_to_bytes(5);
assert_eq!(bytes, &b"hello"[..]);
assert_eq!(buf, &b" world"[..])
}
#[test]
#[should_panic]
fn copy_to_bytes_overflow() {
let mut buf = &b"hello world"[..];
let _bytes = buf.copy_to_bytes(12);
fn copy_to_bytes_mut() {
let mut bytes_mut = BytesMut::from(b"foobar".as_slice());
let ptr = bytes_mut.as_ptr();
let ret = bytes_mut.copy_to_bytes(bytes_mut.len());
assert_eq!(ret.as_ptr(), ptr);
drop(bytes_mut);
let bytes_mut2 = BytesMut::from(ret);
assert_eq!(bytes_mut2.as_ptr(), ptr);
}
+9 -1
View File
@@ -4,7 +4,6 @@ use bytes::buf::UninitSlice;
use bytes::{BufMut, BytesMut};
use core::fmt::Write;
use core::mem::MaybeUninit;
use core::usize;
#[test]
fn test_vec_as_mut_buf() {
@@ -274,3 +273,12 @@ fn copy_from_slice_panics_if_different_length_2() {
let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) };
slice.copy_from_slice(b"abcd");
}
/// Test if with zero capacity BytesMut does not infinitely recurse in put from Buf
#[test]
fn test_bytes_mut_reuse() {
let mut buf = BytesMut::new();
buf.put(&[] as &[u8]);
let mut buf = BytesMut::new();
buf.put(&[1u8, 2, 3] as &[u8]);
}
+319 -2
View File
@@ -1,8 +1,10 @@
#![warn(rust_2018_idioms)]
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::usize;
use std::panic::{self, AssertUnwindSafe};
const LONG: &[u8] = b"mary had a little lamb, little lamb, little lamb";
const SHORT: &[u8] = b"hello world";
@@ -79,7 +81,6 @@ fn fmt() {
#[test]
fn fmt_write() {
use std::fmt::Write;
use std::iter::FromIterator;
let s = String::from_iter((0..10).map(|_| "abcdefg"));
let mut a = BytesMut::with_capacity(64);
@@ -154,6 +155,13 @@ fn slice_oob_2() {
a.slice(44..49);
}
#[test]
#[should_panic]
fn slice_start_greater_than_end() {
let a = Bytes::from(&b"hello world"[..]);
a.slice(5..3);
}
#[test]
fn split_off() {
let mut hello = Bytes::from(&b"helloworld"[..]);
@@ -176,6 +184,13 @@ fn split_off_oob() {
let _ = hello.split_off(44);
}
#[test]
#[should_panic = "split_off out of bounds"]
fn bytes_mut_split_off_oob() {
let mut hello = BytesMut::from(&b"helloworld"[..]);
let _ = hello.split_off(44);
}
#[test]
fn split_off_uninitialized() {
let mut bytes = BytesMut::with_capacity(1024);
@@ -288,6 +303,7 @@ fn split_to_uninitialized() {
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore)]
fn split_off_to_at_gt_len() {
fn make_bytes() -> Bytes {
let mut bytes = BytesMut::with_capacity(100);
@@ -591,6 +607,23 @@ fn extend_from_slice_mut() {
}
}
#[test]
fn extend_from_within_normal() {
let mut bytes = BytesMut::new();
bytes.extend_from_slice(&LONG[..23]);
bytes.extend_from_within(10..22);
bytes.extend_from_within(22..35);
assert_eq!(LONG[..], *bytes);
}
#[test]
#[should_panic]
fn extend_from_within_out_of_range() {
let mut bytes = BytesMut::new();
bytes.extend_from_slice(&LONG[..23]);
bytes.extend_from_within(23..=23);
}
#[test]
fn extend_mut_from_bytes() {
let mut bytes = BytesMut::with_capacity(0);
@@ -720,10 +753,20 @@ fn advance_past_len() {
a.advance(20);
}
#[test]
#[should_panic]
fn mut_advance_past_len() {
let mut a = BytesMut::from("hello world");
unsafe {
a.advance_mut(20);
}
}
#[test]
// Only run these tests on little endian systems. CI uses qemu for testing
// big endian... and qemu doesn't really support threading all that well.
#[cfg(any(miri, target_endian = "little"))]
#[cfg(not(target_family = "wasm"))] // wasm without experimental threads proposal doesn't support threads
fn stress() {
// Tests promoting a buffer from a vec -> shared in a concurrent situation
use std::sync::{Arc, Barrier};
@@ -1400,6 +1443,26 @@ fn try_reclaim_arc() {
assert_eq!(true, buf.try_reclaim(6));
}
#[test]
fn slice_empty_addr() {
let buf = Bytes::from(vec![0; 1024]);
let ptr_start = buf.as_ptr();
let ptr_end = ptr_start.wrapping_add(1024);
let empty_end = buf.slice(1024..);
assert_eq!(empty_end.len(), 0);
assert_eq!(empty_end.as_ptr(), ptr_end);
let empty_start = buf.slice(..0);
assert_eq!(empty_start.len(), 0);
assert_eq!(empty_start.as_ptr(), ptr_start);
// Is miri happy about the provenance?
let _ = &empty_end[..];
let _ = &empty_start[..];
}
#[test]
fn split_off_empty_addr() {
let mut buf = Bytes::from(vec![0; 1024]);
@@ -1479,3 +1542,257 @@ fn split_to_empty_addr_mut() {
let _ = &empty_start[..];
let _ = &buf[..];
}
#[test]
fn bytes_mut_split_boundary_capacities() {
// VEC mode
for at in [0, 5, 11] {
let mut buf = BytesMut::with_capacity(64);
buf.extend_from_slice(b"hello world");
let other = buf.split_off(at);
assert_eq!(
buf.capacity() + other.capacity(),
64,
"split_off at {} should preserve total capacity",
at
);
}
for at in [0, 5, 11] {
let mut buf = BytesMut::with_capacity(64);
buf.extend_from_slice(b"hello world");
let other = buf.split_to(at);
assert_eq!(
buf.capacity() + other.capacity(),
64,
"split_to at {} should preserve total capacity",
at
);
}
// ARC mode (promote via a no-op split)
for at in [0, 5, 11] {
let mut buf = BytesMut::with_capacity(64);
buf.extend_from_slice(b"hello world");
let _ = buf.split_to(0); // promotes to ARC
let other = buf.split_off(at);
assert_eq!(
buf.capacity() + other.capacity(),
64,
"ARC split_off at {} should preserve total capacity",
at
);
}
for at in [0, 5, 11] {
let mut buf = BytesMut::with_capacity(64);
buf.extend_from_slice(b"hello world");
let _ = buf.split_to(0); // promotes to ARC
let other = buf.split_to(at);
assert_eq!(
buf.capacity() + other.capacity(),
64,
"ARC split_to at {} should preserve total capacity",
at
);
}
}
#[derive(Clone)]
struct SharedAtomicCounter(Arc<AtomicUsize>);
impl SharedAtomicCounter {
pub fn new() -> Self {
SharedAtomicCounter(Arc::new(AtomicUsize::new(0)))
}
pub fn increment(&self) {
self.0.fetch_add(1, Ordering::AcqRel);
}
pub fn get(&self) -> usize {
self.0.load(Ordering::Acquire)
}
}
#[derive(Clone)]
struct OwnedTester<const L: usize> {
buf: [u8; L],
drop_count: SharedAtomicCounter,
pub panic_as_ref: bool,
}
impl<const L: usize> OwnedTester<L> {
fn new(buf: [u8; L], drop_count: SharedAtomicCounter) -> Self {
Self {
buf,
drop_count,
panic_as_ref: false,
}
}
}
impl<const L: usize> AsRef<[u8]> for OwnedTester<L> {
fn as_ref(&self) -> &[u8] {
if self.panic_as_ref {
panic!("test-triggered panic in `AsRef<[u8]> for OwnedTester`");
}
self.buf.as_slice()
}
}
impl<const L: usize> Drop for OwnedTester<L> {
fn drop(&mut self) {
self.drop_count.increment();
}
}
#[test]
fn owned_is_unique_always_false() {
let b1 = Bytes::from_owner([1, 2, 3, 4, 5, 6, 7]);
assert!(!b1.is_unique()); // even if ref_cnt == 1
let b2 = b1.clone();
assert!(!b1.is_unique());
assert!(!b2.is_unique());
drop(b1);
assert!(!b2.is_unique()); // even if ref_cnt == 1
}
#[test]
fn owned_buf_sharing() {
let buf = [1, 2, 3, 4, 5, 6, 7];
let b1 = Bytes::from_owner(buf);
let b2 = b1.clone();
assert_eq!(&buf[..], &b1[..]);
assert_eq!(&buf[..], &b2[..]);
assert_eq!(b1.as_ptr(), b2.as_ptr());
assert_eq!(b1.len(), b2.len());
assert_eq!(b1.len(), buf.len());
}
#[test]
fn owned_buf_slicing() {
let b1 = Bytes::from_owner(SHORT);
assert_eq!(SHORT, &b1[..]);
let b2 = b1.slice(1..(b1.len() - 1));
assert_eq!(&SHORT[1..(SHORT.len() - 1)], b2);
assert_eq!(unsafe { SHORT.as_ptr().add(1) }, b2.as_ptr());
assert_eq!(SHORT.len() - 2, b2.len());
}
#[test]
fn owned_dropped_exactly_once() {
let buf: [u8; 5] = [1, 2, 3, 4, 5];
let drop_counter = SharedAtomicCounter::new();
let owner = OwnedTester::new(buf, drop_counter.clone());
let b1 = Bytes::from_owner(owner);
let b2 = b1.clone();
assert_eq!(drop_counter.get(), 0);
drop(b1);
assert_eq!(drop_counter.get(), 0);
let b3 = b2.slice(1..b2.len() - 1);
drop(b2);
assert_eq!(drop_counter.get(), 0);
drop(b3);
assert_eq!(drop_counter.get(), 1);
}
#[test]
fn owned_to_mut() {
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let drop_counter = SharedAtomicCounter::new();
let owner = OwnedTester::new(buf, drop_counter.clone());
let b1 = Bytes::from_owner(owner);
// Holding an owner will fail converting to a BytesMut,
// even when the bytes instance has a ref_cnt == 1.
let b1 = b1.try_into_mut().unwrap_err();
// That said, it's still possible, just not cheap.
let bm1: BytesMut = b1.into();
let new_buf = &bm1[..];
assert_eq!(new_buf, &buf[..]);
// `.into::<BytesMut>()` has correctly dropped the owner
assert_eq!(drop_counter.get(), 1);
}
#[test]
fn owned_to_vec() {
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let drop_counter = SharedAtomicCounter::new();
let owner = OwnedTester::new(buf, drop_counter.clone());
let b1 = Bytes::from_owner(owner);
let v1 = b1.to_vec();
assert_eq!(&v1[..], &buf[..]);
assert_eq!(&v1[..], &b1[..]);
drop(b1);
assert_eq!(drop_counter.get(), 1);
}
#[test]
fn owned_into_vec() {
let drop_counter = SharedAtomicCounter::new();
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let owner = OwnedTester::new(buf, drop_counter.clone());
let b1 = Bytes::from_owner(owner);
let v1: Vec<u8> = b1.into();
assert_eq!(&v1[..], &buf[..]);
// into() vec will copy out of the owner and drop it
assert_eq!(drop_counter.get(), 1);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore)]
fn owned_safe_drop_on_as_ref_panic() {
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let drop_counter = SharedAtomicCounter::new();
let mut owner = OwnedTester::new(buf, drop_counter.clone());
owner.panic_as_ref = true;
let result = panic::catch_unwind(AssertUnwindSafe(|| {
let _ = Bytes::from_owner(owner);
}));
assert!(result.is_err());
assert_eq!(drop_counter.get(), 1);
}
/// Test `BytesMut::put` reuses allocation of `Bytes`.
#[test]
fn bytes_mut_put_bytes_specialization() {
let mut vec = Vec::with_capacity(1234);
vec.push(10);
let capacity = vec.capacity();
assert!(capacity >= 1234);
// Make `Bytes` backed by `Vec`.
let bytes = Bytes::from(vec);
let mut bytes_mut = BytesMut::new();
bytes_mut.put(bytes);
// Check contents is correct.
assert_eq!(&[10], bytes_mut.as_ref());
// If allocation is reused, capacity should be equal to original vec capacity.
assert_eq!(bytes_mut.capacity(), capacity);
}
#[test]
#[should_panic]
fn bytes_mut_reserve_overflow() {
let mut a = BytesMut::from(&b"hello world"[..]);
let mut b = a.split_off(5);
// Ensure b becomes the unique owner of the backing storage
drop(a);
// Trigger overflow in new_cap + offset inside reserve
b.reserve(usize::MAX - 6);
// This call relies on the corrupted cap and may cause UB & HBO
b.put_u8(b'h');
}
+5 -2
View File
@@ -1,3 +1,4 @@
#![cfg(not(miri))]
use std::alloc::{GlobalAlloc, Layout, System};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
@@ -7,7 +8,7 @@ use bytes::{Buf, Bytes};
#[global_allocator]
static LEDGER: Ledger = Ledger::new();
const LEDGER_LENGTH: usize = 2048;
const LEDGER_LENGTH: usize = 1024 * 1024;
struct Ledger {
alloc_table: [(AtomicPtr<u8>, AtomicUsize); LEDGER_LENGTH],
@@ -31,9 +32,11 @@ impl Ledger {
.is_ok()
{
entry_size.store(size, Ordering::SeqCst);
break;
return;
}
}
panic!("Ledger ran out of space.");
}
fn remove(&self, ptr: *mut u8) -> usize {
+1 -1
View File
@@ -135,7 +135,7 @@ fn vectored_read() {
#[test]
fn chain_growing_buffer() {
let mut buff = [' ' as u8; 10];
let mut buff = [b' '; 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
+86
View File
@@ -0,0 +1,86 @@
#![warn(rust_2018_idioms)]
use bytes::{buf::Limit, BufMut};
#[test]
fn long_limit() {
let buf = &mut [0u8; 10];
let limit = buf.limit(100);
assert_eq!(10, limit.remaining_mut());
assert_eq!(&[0u8; 10], &limit.get_ref()[..]);
}
#[test]
fn limit_get_mut() {
let buf = &mut [0u8; 128];
let mut limit = buf.limit(10);
assert_eq!(10, limit.remaining_mut());
assert_eq!(&mut [0u8; 128], &limit.get_mut()[..]);
}
#[test]
fn limit_set_limit() {
let buf = &mut [0u8; 128];
let mut limit = buf.limit(10);
assert_eq!(10, Limit::limit(&limit));
limit.set_limit(5);
assert_eq!(5, Limit::limit(&limit));
}
#[test]
fn limit_chunk_mut() {
let buf = &mut [0u8; 20];
let mut limit = buf.limit(10);
assert_eq!(10, limit.chunk_mut().len());
let buf = &mut [0u8; 10];
let mut limit = buf.limit(20);
assert_eq!(10, limit.chunk_mut().len());
}
#[test]
#[should_panic = "advance out of bounds"]
fn limit_advance_mut_panic_1() {
let buf = &mut [0u8; 10];
let mut limit = buf.limit(100);
unsafe {
limit.advance_mut(50);
}
}
#[test]
#[should_panic = "cnt <= self.limit"]
fn limit_advance_mut_panic_2() {
let buf = &mut [0u8; 100];
let mut limit = buf.limit(10);
unsafe {
limit.advance_mut(50);
}
}
#[test]
fn limit_advance_mut() {
let buf = &mut [0u8; 100];
let mut limit = buf.limit(10);
unsafe {
limit.advance_mut(5);
}
assert_eq!(5, limit.remaining_mut());
assert_eq!(5, limit.chunk_mut().len());
}
#[test]
fn limit_into_inner() {
let buf_arr = *b"hello world";
let buf: &mut [u8] = &mut buf_arr.clone();
let mut limit = buf.limit(4);
let mut dst = vec![];
unsafe {
limit.advance_mut(2);
}
let buf = limit.into_inner();
dst.put(&buf[..]);
assert_eq!(*dst, b"llo world"[..]);
}
+9
View File
@@ -27,3 +27,12 @@ fn buf_read() {
reader.read_line(&mut line).unwrap();
assert_eq!("world", &line);
}
#[test]
fn get_mut() {
let buf = &b"hello world"[..];
let mut reader = buf.reader();
let buf_mut = reader.get_mut();
assert_eq!(11, buf_mut.remaining());
assert_eq!(b"hello world", buf_mut);
}
+52
View File
@@ -30,3 +30,55 @@ fn take_copy_to_bytes_panics() {
let abcd = Bytes::copy_from_slice(b"abcd");
abcd.take(2).copy_to_bytes(3);
}
#[cfg(feature = "std")]
#[test]
fn take_chunks_vectored() {
fn chain() -> impl Buf {
Bytes::from([1, 2, 3].to_vec()).chain(Bytes::from([4, 5, 6].to_vec()))
}
{
let mut dst = [std::io::IoSlice::new(&[]); 2];
let take = chain().take(0);
assert_eq!(take.chunks_vectored(&mut dst), 0);
}
{
let mut dst = [std::io::IoSlice::new(&[]); 2];
let take = chain().take(1);
assert_eq!(take.chunks_vectored(&mut dst), 1);
assert_eq!(&*dst[0], &[1]);
}
{
let mut dst = [std::io::IoSlice::new(&[]); 2];
let take = chain().take(3);
assert_eq!(take.chunks_vectored(&mut dst), 1);
assert_eq!(&*dst[0], &[1, 2, 3]);
}
{
let mut dst = [std::io::IoSlice::new(&[]); 2];
let take = chain().take(4);
assert_eq!(take.chunks_vectored(&mut dst), 2);
assert_eq!(&*dst[0], &[1, 2, 3]);
assert_eq!(&*dst[1], &[4]);
}
{
let mut dst = [std::io::IoSlice::new(&[]); 2];
let take = chain().take(6);
assert_eq!(take.chunks_vectored(&mut dst), 2);
assert_eq!(&*dst[0], &[1, 2, 3]);
assert_eq!(&*dst[1], &[4, 5, 6]);
}
{
let mut dst = [std::io::IoSlice::new(&[]); 2];
let take = chain().take(7);
assert_eq!(take.chunks_vectored(&mut dst), 2);
assert_eq!(&*dst[0], &[1, 2, 3]);
assert_eq!(&*dst[1], &[4, 5, 6]);
}
}