Compare commits

...
78 Commits
Author SHA1 Message Date
d5c8ad3227 Release shared reference on zero truncate (#842)
Co-authored-by: JSap0914 <[email protected]>
2026-07-16 12:19:37 +02:00
bestgopherandGitHub 002df10b8c Simplify shared alignment assertions (#841)
Replace array-underflow checks with const assert expressions.
Add diagnostics that explain the pointer-tagging alignment invariant.

Signed-off-by: bestgopher <[email protected]>
2026-07-14 09:45:41 +02:00
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
Alice RyhlandGitHub c45697ce42 chore: prepare bytes v1.8.0 (#741) 2024-10-21 21:54:34 +02:00
Alice RyhlandGitHub 0ac54ca706 Guarantee address in split_off/split_to for empty slices (#740)
Signed-off-by: Alice Ryhl <[email protected]>
2024-10-18 19:46:49 +02:00
Paolo BarboliniandGitHub d7c1d658d9 chore: prepare bytes v1.7.2 (#736) 2024-09-18 07:39:32 +02:00
Taiki EndoandGitHub ac46ebdd46 ci: update nightly to nightly-2024-09-15 (#734) 2024-09-17 11:02:45 -04:00
Paolo BarboliniandGitHub 79fb85323c fix: apply sign extension when decoding int (#732)
Closes #730
2024-08-30 08:20:29 -04:00
Paolo BarboliniandGitHub 291df5acc9 Fix double spaces in comments and doc comments (#731) 2024-08-19 10:19:35 +02:00
Cameron BythewayandGitHub ed7d5ff39e test: ensure BytesMut::advance reduces capacity (#728) 2024-08-02 19:07:45 +00:00
Alice RyhlandGitHub dc4fb3e8f4 chore: prepare bytes v1.7.1 (#727) 2024-08-01 22:55:24 +02:00
Alice RyhlandGitHub f488be48d0 Revert "Reuse capacity when possible in <BytesMut as Buf>::advance impl" (#726)
This reverts commit baa5053572.
2024-08-01 22:38:17 +02:00
Motoyuki KimuraandGitHub 03fdde9dcf chore: prepare v1.7.0 (#724) 2024-07-31 13:33:47 +02:00
Alice Ryhl f8c7b574c0 Merge 'v1.6.1' into 'master' (#721) 2024-07-13 09:51:48 +02:00
EXPLOSIONandGitHub 9965a04b56 Remove unnecessary file (#719) 2024-07-10 19:08:47 +09:00
vvvviivandGitHub 3443ca5a0b docs: clarify the behavior of Buf::chunk (#717) 2024-07-09 14:13:09 +02:00
Sebastian HahnandGitHub 8cc940779f Allow reclaiming the current allocation (#686) 2024-06-28 14:26:32 +02:00
Paolo BarboliniandGitHub 7a5154ba8b Clarify how BytesMut::zeroed works and advantages to manual impl (#714) 2024-06-21 14:07:25 +02:00
Anthony RamineandGitHub fa1daac3ae Change Bytes::make_mut to impl From<Bytes> for BytesMut (closes #709) (#710)
<Arc<T>>::make_mut returns a &mut T, such an API is doable for Bytes too
and thus we should reserve Bytes::make_mut for that.

Furthermore, it would be helpful to use From<Bytes> as a trait bound
in some cases with other traits such as Hyper's body trait, where Hyper
gives you Bytes values.

Finally, making it impl From<Bytes> for BytesMut means the API is more
easily discoverable as it appears on both Bytes and BytesMut.
2024-05-28 10:14:02 +02:00
Paolo BarboliniandGitHub caf520ac7f Fix iter tests to use the actual bytes IntoIter instead of std (#707) 2024-05-19 15:28:03 -04:00
Brad DunbarandGitHub 4950c50376 Offset from (#705) 2024-05-11 19:41:50 +02:00
Émile FugulinandGitHub 86694b0564 Add zero-copy make_mut (#695) 2024-05-05 17:58:00 +02:00
Alice RyhlandGitHub 0c17e99283 ci: silence unexpected-cfgs warnings due to #[cfg(loom)] (#703) 2024-05-05 14:19:18 +02:00
Alice RyhlandGitHub cb7f8449b5 Tweak clear and truncate length modifications (#700) 2024-04-26 09:24:05 +02:00
Paolo BarboliniandGitHub a8806c2457 Improve BytesMut::split suggestion (#699) 2024-04-25 10:43:15 +02:00
Paolo BarboliniandGitHub baa5053572 Reuse capacity when possible in <BytesMut as Buf>::advance impl (#698) 2024-04-25 09:08:16 +02:00
Brad DunbarandGitHub ce09d7d358 Bytes::split_off - check fast path first (#693)
Follow up to https://github.com/tokio-rs/bytes/pull/689

* If `at == self.len()`, we already know `at <= self.len()`.
* If `at == 0`, we already know `at <= self.len()`.
2024-04-24 08:23:39 -04:00
9d3ec1cffb Resize refactor (#696)
* use checked_sub

* return when additional == 0

* move safe operation out of unsafe block

* use spare_capacity_mut instead of chunk_mut

We don't need to check capacity because it's already been reserved
above.

* Add safety comments

* refactor to use guard clauses

This would be better written with let-else, but we won't get that until
`MSRV >= 1.65.x`.

* use if-let instead of unwrap

* reduce scope of unsafe blocks

Co-authored-by: Alice Ryhl <[email protected]>

---------

Co-authored-by: Alice Ryhl <[email protected]>
2024-04-24 05:49:53 -04:00
Brad DunbarandGitHub 4e2c9c065a Truncate tweaks (#694) 2024-04-17 11:27:00 +02:00
Joseph PerezandGitHub 327615e5d4 test(benches): encloses bytes into test::black_box for clone benches (#691)
Closes #690
Without it, it seems to me that compiler is able to inline the vtable,
resulting in similar results for `clone_shared` and `clone_arg_vec`.
2024-04-11 11:45:18 +02:00
tisonandGitHub b5fbfc3edb perf: improve Bytes::copy_to_bytes (#688)
Signed-off-by: tison <[email protected]>
2024-04-10 16:45:31 +02:00
Brad DunbarandGitHub 4eb62b912a Bytes::split_to - check fast path first (#689)
If `at == self.len()` then we already know `at <= self.len()`. If
`at == 0`, it can't be greater than `self.len()`.
2024-04-10 10:09:09 +02:00
Brad DunbarandGitHub e4af48633c Don't set len in BytesMut::reserve (#682)
A fundamental invariant of `reserve` is that it can extend capacity
while the stored data remains the same, even if it's moved to a new
allocation. As a result, `len` can never change during a call to
`reserve`.
2024-04-09 14:35:54 +02:00
Brad DunbarandGitHub 0d4cc7ffed Bytes: Use ManuallyDrop instead of mem::forget (#678) 2024-04-08 17:05:04 +02:00
33 changed files with 4003 additions and 431 deletions
+50 -19
View File
@@ -13,7 +13,7 @@ on:
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
nightly: nightly-2022-11-12
nightly: nightly-2024-09-15
defaults:
run:
@@ -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:
@@ -136,6 +164,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Miri
run: ci/miri.sh
@@ -160,6 +190,7 @@ jobs:
- minrust
- cross
- tsan
- miri
- loom
runs-on: ubuntu-latest
steps:
+134
View File
@@ -1,3 +1,137 @@
# 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)
# 1.7.2 (September 17, 2024)
### Fixed
- Fix default impl of `Buf::{get_int, get_int_le}` (#732)
### Documented
- Fix double spaces in comments and doc comments (#731)
### Internal changes
- Ensure BytesMut::advance reduces capacity (#728)
# 1.7.1 (August 1, 2024)
This release reverts the following change due to a regression:
- Reuse capacity when possible in `<BytesMut as Buf>::advance` impl (#698)
The revert can be found at #726.
# 1.7.0 (July 31, 2024)
### Added
- Add conversion from `Bytes` to `BytesMut` (#695, #710)
- Add reclaim method without additional allocation (#686)
### Documented
- Clarify how `BytesMut::zeroed` works (#714)
- Clarify the behavior of `Buf::chunk` (#717)
### Changed
- Change length condition of `BytesMut::truncate`
- Reuse capacity when possible in `<BytesMut as Buf>::advance` impl (#698)
- Improve `must_use` suggestion of `BytesMut::split` (#699)
### Internal changes
- Use `ManuallyDrop` instead of `mem::forget` (#678)
- Don't set `len` in `BytesMut::reserve` (#682)
- Optimize `Bytes::copy_to_bytes` (#688)
- Refactor `BytesMut::truncate` (#694)
- Refactor `BytesMut::resize` (#696)
- Reorder assertion in `Bytes::split_to`, `Bytes::split_off` (#689, #693)
- Use `offset_from` in more places (#705)
- Correct the wrong usage of `IntoIter` (#707)
# 1.6.1 (July 13, 2024)
This release fixes a bug where `Bytes::is_unique` returns incorrect values when
+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.6.1"
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 -3
View File
@@ -47,7 +47,7 @@ fn clone_static(b: &mut Bencher) {
b.iter(|| {
for _ in 0..1024 {
test::black_box(&bytes.clone());
test::black_box(test::black_box(&bytes).clone());
}
})
}
@@ -58,7 +58,7 @@ fn clone_shared(b: &mut Bencher) {
b.iter(|| {
for _ in 0..1024 {
test::black_box(&bytes.clone());
test::black_box(test::black_box(&bytes).clone());
}
})
}
@@ -70,7 +70,7 @@ fn clone_arc_vec(b: &mut Bencher) {
b.iter(|| {
for _ in 0..1024 {
test::black_box(&bytes.clone());
test::black_box(test::black_box(&bytes).clone());
}
})
}
+4 -2
View File
@@ -1,11 +1,13 @@
#!/bin/bash
set -e
rustup toolchain install nightly --component miri
rustup override set nightly
rustup component add miri
cargo miri setup
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"
+1545 -42
View File
File diff suppressed because it is too large Load Diff
+51 -21
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};
@@ -165,7 +165,7 @@ pub unsafe trait BufMut {
///
/// # Implementer notes
///
/// This function should never panic. `chunk_mut` should return an empty
/// This function should never panic. `chunk_mut()` should return an empty
/// slice **if and only if** `remaining_mut()` returns 0. In other words,
/// `chunk_mut()` returning an empty slice implies that `remaining_mut()` will
/// return 0 and `remaining_mut()` returning 0 implies that `chunk_mut()` will
@@ -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 {
@@ -1107,7 +1116,7 @@ pub unsafe trait BufMut {
}
}
/// Writes an IEEE754 single-precision (4 bytes) floating point number to
/// Writes an IEEE754 single-precision (4 bytes) floating point number to
/// `self` in big-endian byte order.
///
/// The current position is advanced by 4.
@@ -1131,7 +1140,7 @@ pub unsafe trait BufMut {
self.put_u32(n.to_bits());
}
/// Writes an IEEE754 single-precision (4 bytes) floating point number to
/// Writes an IEEE754 single-precision (4 bytes) floating point number to
/// `self` in little-endian byte order.
///
/// The current position is advanced by 4.
@@ -1183,7 +1192,7 @@ pub unsafe trait BufMut {
self.put_u32_ne(n.to_bits());
}
/// Writes an IEEE754 double-precision (8 bytes) floating point number to
/// Writes an IEEE754 double-precision (8 bytes) floating point number to
/// `self` in big-endian byte order.
///
/// The current position is advanced by 8.
@@ -1207,7 +1216,7 @@ pub unsafe trait BufMut {
self.put_u64(n.to_bits());
}
/// Writes an IEEE754 double-precision (8 bytes) floating point number to
/// Writes an IEEE754 double-precision (8 bytes) floating point number to
/// `self` in little-endian byte order.
///
/// The current position is advanced by 8.
@@ -1231,7 +1240,7 @@ pub unsafe trait BufMut {
self.put_u64_le(n.to_bits());
}
/// Writes an IEEE754 double-precision (8 bytes) floating point number to
/// Writes an IEEE754 double-precision (8 bytes) floating point number to
/// `self` in native-endian byte order.
///
/// The current position is advanced by 8.
@@ -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
}
}
+1 -1
View File
@@ -110,7 +110,7 @@ impl UninitSlice {
unsafe { self[index..].as_mut_ptr().write(byte) }
}
/// Copies bytes from `src` into `self`.
/// Copies bytes from `src` into `self`.
///
/// The length of `src` must be the same as `self`.
///
+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)
}
+454 -141
View File
@@ -1,6 +1,7 @@
use core::iter::FromIterator;
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::ops::{Deref, RangeBounds};
use core::{cmp, fmt, hash, mem, ptr, slice, usize};
use core::ptr::NonNull;
use core::{cmp, fmt, hash, ptr, slice};
use alloc::{
alloc::{dealloc, Layout},
@@ -14,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::Buf;
use crate::{Buf, BytesMut};
/// A cheaply cloneable and sliceable chunk of contiguous memory.
///
@@ -105,17 +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>,
/// `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 {
@@ -140,6 +146,7 @@ impl Bytes {
Bytes::from_static(EMPTY)
}
/// Creates a new empty `Bytes`.
#[cfg(all(loom, test))]
pub fn new() -> Self {
const EMPTY: &[u8] = &[];
@@ -170,6 +177,7 @@ impl Bytes {
}
}
/// Creates a new `Bytes` from a static slice.
#[cfg(all(loom, test))]
pub fn from_static(bytes: &'static [u8]) -> Self {
Bytes {
@@ -180,6 +188,107 @@ impl Bytes {
}
}
/// Creates a new `Bytes` with length zero and the given pointer as the address.
fn new_empty_with_ptr(ptr: *const u8) -> Self {
debug_assert!(!ptr.is_null());
// Detach this pointer's provenance from whichever allocation it came from, and reattach it
// to the provenance of the fake ZST [u8;0] at the same address.
let ptr = without_provenance(ptr as usize);
Bytes {
ptr,
len: 0,
data: AtomicPtr::new(ptr::null_mut()),
vtable: &STATIC_VTABLE,
}
}
/// 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
@@ -210,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
///
/// ```
@@ -260,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();
@@ -362,7 +446,9 @@ impl Bytes {
/// Splits the bytes into two at the given index.
///
/// Afterwards `self` contains elements `[0, at)`, and the returned `Bytes`
/// contains elements `[at, len)`.
/// contains elements `[at, len)`. It's guaranteed that the memory does not
/// move, that is, the address of `self` does not change, and the address of
/// the returned slice is `at` bytes after that.
///
/// This is an `O(1)` operation that just increases the reference count and
/// sets a few indices.
@@ -384,6 +470,14 @@ impl Bytes {
/// Panics if `at > len`.
#[must_use = "consider Bytes::truncate if you don't need the other half"]
pub fn split_off(&mut self, at: usize) -> Self {
if at == self.len() {
return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(at));
}
if at == 0 {
return mem::replace(self, Bytes::new_empty_with_ptr(self.ptr));
}
assert!(
at <= self.len(),
"split_off out of bounds: {:?} <= {:?}",
@@ -391,18 +485,12 @@ impl Bytes {
self.len(),
);
if at == self.len() {
return Bytes::new();
}
if at == 0 {
return mem::replace(self, Bytes::new());
}
let mut ret = self.clone();
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
@@ -433,6 +521,15 @@ impl Bytes {
/// Panics if `at > len`.
#[must_use = "consider Bytes::advance if you don't need the other half"]
pub fn split_to(&mut self, at: usize) -> Self {
if at == self.len() {
let end_ptr = self.ptr.wrapping_add(at);
return mem::replace(self, Bytes::new_empty_with_ptr(end_ptr));
}
if at == 0 {
return Bytes::new_empty_with_ptr(self.ptr);
}
assert!(
at <= self.len(),
"split_to out of bounds: {:?} <= {:?}",
@@ -440,16 +537,10 @@ impl Bytes {
self.len(),
);
if at == self.len() {
return mem::replace(self, Bytes::new());
}
if at == 0 {
return Bytes::new();
}
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;
@@ -476,7 +567,9 @@ impl Bytes {
/// ```
#[inline]
pub fn truncate(&mut self, len: usize) {
if len < self.len {
if len == 0 {
drop(mem::replace(self, Bytes::new_empty_with_ptr(self.ptr)));
} else if len < self.len {
// The Vec "promotable" vtables do not store the capacity,
// so we cannot truncate while using this repr. We *have* to
// promote using `split_off` so the capacity can be stored.
@@ -506,6 +599,32 @@ impl Bytes {
self.truncate(0);
}
/// Try to convert self into `BytesMut`.
///
/// If `self` is unique for the entire original buffer, this will succeed
/// and return a `BytesMut` with the contents of `self` without copying.
/// 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
///
/// ```
/// use bytes::{Bytes, BytesMut};
///
/// let bytes = Bytes::from(b"hello".to_vec());
/// assert_eq!(bytes.try_into_mut(), Ok(BytesMut::from(&b"hello"[..])));
/// ```
pub fn try_into_mut(self) -> Result<BytesMut, Bytes> {
if self.is_unique() {
Ok(self.into())
} else {
Err(self)
}
}
#[inline]
pub(crate) unsafe fn with_vtable(
ptr: *const u8,
@@ -535,6 +654,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
@@ -544,7 +668,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) }
}
}
@@ -581,13 +706,7 @@ impl Buf for Bytes {
}
fn copy_to_bytes(&mut self, len: usize) -> Self {
if len == self.remaining() {
core::mem::replace(self, Bytes::new())
} else {
let ret = self.slice(..len);
self.advance(len);
ret
}
self.split_to(len)
}
}
@@ -656,7 +775,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))
}
}
@@ -828,24 +947,27 @@ impl From<&'static str> for Bytes {
}
impl From<Vec<u8>> for Bytes {
fn from(mut vec: Vec<u8>) -> 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 {
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),
});
mem::forget(vec);
let shared = Box::into_raw(shared);
// The pointer should be aligned, so this assert should
// always succeed.
debug_assert!(
@@ -892,6 +1014,29 @@ impl From<Box<[u8]>> for Bytes {
}
}
impl From<Bytes> for BytesMut {
/// Convert self into `BytesMut`.
///
/// If `bytes` is unique for the entire original buffer, this will return a
/// `BytesMut` with the contents of `bytes` without copying.
/// If `bytes` is not unique for the entire original buffer, this will make
/// a copy of `bytes` subset of the original buffer in a new `BytesMut`.
///
/// # Examples
///
/// ```
/// use bytes::{Bytes, BytesMut};
///
/// let bytes = Bytes::from(b"hello".to_vec());
/// assert_eq!(BytesMut::from(bytes), BytesMut::from(&b"hello"[..]));
/// ```
fn from(bytes: Bytes) -> Self {
let mut bytes = ManuallyDrop::new(bytes);
let data = bytes.data_mut();
unsafe { (bytes.vtable.into_mut)(data, bytes.ptr, bytes.len) }
}
}
impl From<String> for Bytes {
fn from(s: String) -> Bytes {
Bytes::from(s.into_bytes())
@@ -900,8 +1045,9 @@ 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) }
let mut bytes = ManuallyDrop::new(bytes);
let data = bytes.data_mut();
unsafe { (bytes.vtable.into_vec)(data, bytes.ptr, bytes.len) }
}
}
@@ -920,7 +1066,8 @@ impl fmt::Debug for Vtable {
const STATIC_VTABLE: Vtable = Vtable {
clone: static_clone,
to_vec: static_to_vec,
into_vec: static_to_vec,
into_mut: static_to_mut,
is_unique: static_is_unique,
drop: static_drop,
};
@@ -930,31 +1077,108 @@ 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(_: *mut (), ptr: *const u8, len: usize) -> BytesMut {
let slice = slice::from_raw_parts(ptr, len);
BytesMut::from(slice)
}
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,
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,
into_vec: promotable_odd_to_vec,
into_mut: promotable_odd_to_mut,
is_unique: promotable_is_unique,
drop: promotable_odd_drop,
};
@@ -973,12 +1197,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 {
@@ -989,7 +1212,7 @@ unsafe fn promotable_to_vec(
let buf = f(shared);
let cap = (ptr as usize - buf as usize) + len;
let cap = ptr.offset_from(buf) as usize + len;
// Copy back buffer
ptr::copy(ptr, buf, len);
@@ -998,25 +1221,56 @@ unsafe fn promotable_to_vec(
}
}
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_to_mut(
shared: *mut (),
ptr: *const u8,
len: usize,
f: fn(*mut ()) -> *mut u8,
) -> BytesMut {
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
shared_to_mut_impl(shared.cast(), ptr, len)
} else {
// KIND_VEC is a view of an underlying buffer at a certain offset.
// The ptr + len always represents the end of that buffer.
// Before truncating it, it is first promoted to KIND_ARC.
// Thus, we can safely reconstruct a Vec from it without leaking memory.
debug_assert_eq!(kind, KIND_VEC);
let buf = f(shared);
let off = ptr.offset_from(buf) as usize;
let cap = off + len;
let v = Vec::from_raw_parts(buf, cap, cap);
let mut b = BytesMut::from_vec(v);
b.advance_unchecked(off);
b
}
}
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_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_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)
})
}
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_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);
}
}
unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
@@ -1031,23 +1285,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_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_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
promotable_to_mut(shared, ptr, len, |shared| shared.cast())
}
if kind == KIND_ARC {
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
unsafe fn promotable_odd_drop(shared: *mut (), ptr: *const u8, len: usize) {
let kind = shared as usize & KIND_MASK;
free_boxed_slice(shared.cast(), ptr, len);
}
});
if kind == KIND_ARC {
release_shared(shared.cast());
} else {
debug_assert_eq!(kind, KIND_VEC);
free_boxed_slice(shared.cast(), ptr, len);
}
}
unsafe fn promotable_is_unique(data: &AtomicPtr<()>) -> bool {
@@ -1063,7 +1318,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 as usize - buf as usize) + len;
let cap = offset.offset_from(buf) as usize + len;
dealloc(buf, Layout::from_size_align(cap, 1).unwrap())
}
@@ -1076,6 +1331,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()) }
@@ -1086,11 +1350,17 @@ impl Drop for Shared {
// This is a necessary invariant since we depend on allocating `Shared` a
// shared object to implicitly carry the `KIND_ARC` flag in its pointer.
// This flag is set when the LSB is 0.
const _: [(); 0 - mem::align_of::<Shared>() % 2] = []; // Assert that the alignment of `Shared` is divisible by 2.
const _: () = {
assert!(
mem::align_of::<Shared>() % 2 == 0,
"Shared alignment must be divisible by 2 for pointer tagging"
);
};
static SHARED_VTABLE: Vtable = Vtable {
clone: shared_clone,
to_vec: shared_to_vec,
into_vec: shared_to_vec,
into_mut: shared_to_mut,
is_unique: shared_is_unique,
drop: shared_drop,
};
@@ -1116,11 +1386,11 @@ unsafe fn shared_to_vec_impl(shared: *mut Shared, ptr: *const u8, len: usize) ->
.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>));
// Deallocate the `Shared` instance without running its destructor.
let shared = *Box::from_raw(shared);
let shared = ManuallyDrop::new(shared);
let buf = shared.buf;
let cap = shared.cap;
// Copy back buffer
ptr::copy(ptr, buf, len);
@@ -1133,8 +1403,47 @@ 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 {
// The goal is to check if the current handle is the only handle
// that currently has access to the buffer. This is done by
// checking if the `ref_cnt` is currently 1.
//
// The `Acquire` ordering synchronizes with the `Release` as
// part of the `fetch_sub` in `release_shared`. The `fetch_sub`
// operation guarantees that any mutations done in other threads
// are ordered before the `ref_cnt` is decremented. As such,
// this `Acquire` will guarantee that those mutations are
// visible to the current thread.
//
// Otherwise, we take the other branch, copy the data and call `release_shared`.
if (*shared).ref_cnt.load(Ordering::Acquire) == 1 {
// Deallocate the `Shared` instance without running its destructor.
let shared = *Box::from_raw(shared);
let shared = ManuallyDrop::new(shared);
let buf = shared.buf;
let cap = shared.cap;
// Rebuild Vec
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);
b.advance_unchecked(off);
b
} else {
// Copy the data from Shared in a new Vec, then release it
let v = slice::from_raw_parts(ptr, len).to_vec();
release_shared(shared);
BytesMut::from_vec(v)
}
}
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 {
@@ -1143,10 +1452,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 {
@@ -1172,7 +1479,7 @@ unsafe fn shallow_clone_vec(
offset: *const u8,
len: usize,
) -> Bytes {
// If the buffer is still tracked in a `Vec<u8>`. It is time to
// If the buffer is still tracked in a `Vec<u8>`. It is time to
// promote the vec to an `Arc`. This could potentially be called
// concurrently, so some care must be taken.
@@ -1183,16 +1490,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 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`.
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.
@@ -1212,7 +1521,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 {
@@ -1295,6 +1604,10 @@ where
new_addr as *mut u8
}
fn without_provenance(ptr: usize) -> *const u8 {
core::ptr::null::<u8>().wrapping_add(ptr)
}
// compile-fails
/// ```compile_fail
+307 -85
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::{Buf, BufMut, Bytes};
use crate::{Buf, BufMut, Bytes, TryGetError};
/// A unique reference to a contiguous slice of memory.
///
@@ -80,11 +79,25 @@ 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.
// This flag is set when the LSB is 0.
const _: [(); 0 - mem::align_of::<Shared>() % 2] = []; // Assert that the alignment of `Shared` is divisible by 2.
const _: () = {
assert!(
mem::align_of::<Shared>() % 2 == 0,
"Shared alignment must be divisible by 2 for pointer tagging"
);
};
// Buffer storage strategy flags.
const KIND_ARC: usize = 0b0;
@@ -226,7 +239,7 @@ impl BytesMut {
///
/// # Examples
///
/// ```
/// ```ignore-wasm
/// use bytes::{BytesMut, BufMut};
/// use std::thread;
///
@@ -264,7 +277,14 @@ impl BytesMut {
}
}
/// Creates a new `BytesMut`, which is initialized with zero.
/// Creates a new `BytesMut` containing `len` zeros.
///
/// The resulting object has a length of `len` and a capacity greater
/// than or equal to `len`. The entire length of the object will be filled
/// with zeros.
///
/// On some platforms or allocators this function may be faster than
/// a manual implementation.
///
/// # Examples
///
@@ -273,6 +293,7 @@ impl BytesMut {
///
/// let zeros = BytesMut::zeroed(42);
///
/// assert!(zeros.capacity() >= 42);
/// assert_eq!(zeros.len(), 42);
/// zeros.into_iter().for_each(|x| assert_eq!(x, 0));
/// ```
@@ -283,7 +304,9 @@ impl BytesMut {
/// Splits the bytes into two at the given index.
///
/// Afterwards `self` contains elements `[0, at)`, and the returned
/// `BytesMut` contains elements `[at, capacity)`.
/// `BytesMut` contains elements `[at, capacity)`. It's guaranteed that the
/// memory does not move, that is, the address of `self` does not change,
/// and the address of the returned slice is `at` bytes after that.
///
/// This is an `O(1)` operation that just increases the reference count
/// and sets a few indices.
@@ -315,6 +338,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);
@@ -349,7 +376,7 @@ impl BytesMut {
///
/// assert_eq!(other, b"hello world"[..]);
/// ```
#[must_use = "consider BytesMut::advance(len()) if you don't need the other half"]
#[must_use = "consider BytesMut::clear if you don't need the other half"]
pub fn split(&mut self) -> BytesMut {
let len = self.len();
self.split_to(len)
@@ -391,6 +418,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()`.
@@ -423,9 +454,8 @@ impl BytesMut {
/// ```
pub fn truncate(&mut self, len: usize) {
if len <= self.len() {
unsafe {
self.set_len(len);
}
// SAFETY: Shrinking the buffer cannot expose uninitialized bytes.
unsafe { self.set_len(len) };
}
}
@@ -441,7 +471,8 @@ impl BytesMut {
/// assert!(buf.is_empty());
/// ```
pub fn clear(&mut self) {
self.truncate(0);
// SAFETY: Setting the length to zero cannot expose uninitialized bytes.
unsafe { self.set_len(0) };
}
/// Resizes the buffer so that `len` is equal to `new_len`.
@@ -467,18 +498,26 @@ impl BytesMut {
/// assert_eq!(&buf[..], &[0x1, 0x1, 0x3, 0x3]);
/// ```
pub fn resize(&mut self, new_len: usize, value: u8) {
let len = self.len();
if new_len > len {
let additional = new_len - len;
self.reserve(additional);
unsafe {
let dst = self.chunk_mut().as_mut_ptr();
ptr::write_bytes(dst, value, additional);
self.set_len(new_len);
}
let additional = if let Some(additional) = new_len.checked_sub(self.len()) {
additional
} else {
self.truncate(new_len);
return;
};
if additional == 0 {
return;
}
self.reserve(additional);
let dst = self.spare_capacity_mut().as_mut_ptr();
// SAFETY: `spare_capacity_mut` returns a valid, properly aligned pointer and we've
// reserved enough space to write `additional` bytes.
unsafe { ptr::write_bytes(dst, value, additional) };
// SAFETY: There are at least `new_len` initialized bytes in the buffer so no
// uninitialized bytes are being exposed.
unsafe { self.set_len(new_len) };
}
/// Sets the length of the buffer.
@@ -534,6 +573,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.
@@ -581,12 +622,13 @@ impl BytesMut {
return;
}
self.reserve_inner(additional);
// will always succeed
let _ = self.reserve_inner(additional, true);
}
// In separate function to allow the short-circuits in `reserve` to
// be inline-able. Significant helps performance.
fn reserve_inner(&mut self, additional: usize) {
// In separate function to allow the short-circuits in `reserve` and `try_reclaim` to
// be inline-able. Significantly helps performance. Returns false if it did not succeed.
fn reserve_inner(&mut self, additional: usize, allocate: bool) -> bool {
let len = self.len();
let kind = self.kind();
@@ -631,6 +673,9 @@ impl BytesMut {
// can gain capacity back.
self.cap += off;
} else {
if !allocate {
return false;
}
// Not enough space, or reusing might be too much overhead:
// allocate more space!
let mut v =
@@ -639,11 +684,11 @@ impl BytesMut {
// Update the info
self.ptr = vptr(v.as_mut_ptr().add(off));
self.len = v.len() - off;
self.cap = v.capacity() - off;
debug_assert_eq!(self.len, v.len() - off);
}
return;
return true;
}
}
@@ -654,7 +699,11 @@ impl BytesMut {
// allocating a new vector with the requested capacity.
//
// Compute the new capacity
let mut new_cap = len.checked_add(additional).expect("overflow");
let mut new_cap = match len.checked_add(additional) {
Some(new_cap) => new_cap,
None if !allocate => return false,
None => panic!("overflow"),
};
unsafe {
// First, try to reclaim the buffer. This is possible if the current
@@ -668,11 +717,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 {
@@ -685,14 +740,15 @@ impl BytesMut {
self.ptr = vptr(ptr);
self.cap = v.capacity();
} else {
// calculate offset
let off = (self.ptr.as_ptr() as usize) - (v.as_ptr() as usize);
if !allocate {
return false;
}
// 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
@@ -714,18 +770,21 @@ 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;
return true;
}
}
if !allocate {
return false;
}
let original_capacity_repr = unsafe { (*shared).original_capacity_repr };
let original_capacity = original_capacity_from_repr(original_capacity_repr);
@@ -746,8 +805,71 @@ impl BytesMut {
let data = (original_capacity_repr << ORIGINAL_CAPACITY_OFFSET) | KIND_VEC;
self.data = invalid_ptr(data);
self.ptr = vptr(v.as_mut_ptr());
self.len = v.len();
self.cap = v.capacity();
debug_assert_eq!(self.len, v.len());
true
}
/// Attempts to cheaply reclaim already allocated capacity for at least `additional` more
/// bytes to be inserted into the given `BytesMut` and returns `true` if it succeeded.
///
/// `try_reclaim` behaves exactly like `reserve`, except that it never allocates new storage
/// and returns a `bool` indicating whether it was successful in doing so:
///
/// `try_reclaim` returns false under these conditions:
/// - The spare capacity left is less than `additional` bytes AND
/// - The existing allocation cannot be reclaimed cheaply or it was less than
/// `additional` bytes in size
///
/// Reclaiming the allocation cheaply is possible if the `BytesMut` has no outstanding
/// 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
///
/// ```
/// use bytes::BytesMut;
///
/// let mut buf = BytesMut::with_capacity(64);
/// assert_eq!(true, buf.try_reclaim(64));
/// assert_eq!(64, buf.capacity());
///
/// buf.extend_from_slice(b"abcd");
/// let mut split = buf.split();
/// assert_eq!(60, buf.capacity());
/// assert_eq!(4, split.capacity());
/// assert_eq!(false, split.try_reclaim(64));
/// assert_eq!(false, buf.try_reclaim(64));
/// // The split buffer is filled with "abcd"
/// assert_eq!(false, split.try_reclaim(4));
/// // buf is empty and has capacity for 60 bytes
/// assert_eq!(true, buf.try_reclaim(60));
///
/// drop(buf);
/// assert_eq!(false, split.try_reclaim(64));
///
/// split.clear();
/// assert_eq!(4, split.capacity());
/// assert_eq!(true, split.try_reclaim(64));
/// assert_eq!(64, split.capacity());
/// ```
// I tried splitting out try_reclaim_inner after the short circuits, but it was inlined
// regardless with Rust 1.78.0 so probably not worth it
#[inline]
#[must_use = "consider BytesMut::reserve if you need an infallible reservation"]
pub fn try_reclaim(&mut self, additional: usize) -> bool {
let len = self.len();
let rem = self.capacity() - len;
if additional <= rem {
// The handle can already store at least `additional` more bytes, so
// there is no further work needed to be done.
return true;
}
self.reserve_inner(additional, false)
}
/// Appends given bytes to this `BytesMut`.
@@ -784,7 +906,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
@@ -860,7 +1018,7 @@ impl BytesMut {
/// # SAFETY
///
/// The caller must ensure that `count` <= `self.cap`.
unsafe fn advance_unchecked(&mut self, count: usize) {
pub(crate) unsafe fn advance_unchecked(&mut self, count: usize) {
// Setting the start to 0 is a no-op, so return early if this is the
// case.
if count == 0 {
@@ -893,11 +1051,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(());
}
@@ -940,13 +1126,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.
@@ -1078,14 +1269,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;
@@ -1106,11 +1301,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);
}
}
}
@@ -1188,7 +1399,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))
}
}
@@ -1623,10 +1834,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) };
@@ -1668,23 +1879,6 @@ fn invalid_ptr<T>(addr: usize) -> *mut T {
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 stabilized
/// 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.sub(off);
len += off;
@@ -1697,7 +1891,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,
into_vec: shared_v_to_vec,
into_mut: shared_v_to_mut,
is_unique: shared_v_is_unique,
drop: shared_v_drop,
};
@@ -1710,14 +1905,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
@@ -1732,16 +1927,43 @@ unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> V
}
}
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;
// The capacity is always the original capacity of the buffer
// minus the offset from the start of the buffer
let v = &mut shared.vec;
let v_capacity = v.capacity();
let v_ptr = v.as_mut_ptr();
let offset = ptr.offset_from(v_ptr) as usize;
let cap = v_capacity - offset;
let ptr = vptr(ptr as *mut u8);
BytesMut {
ptr,
len,
cap,
data: shared,
}
} else {
let v = slice::from_raw_parts(ptr, len).to_vec();
release_shared(shared);
BytesMut::from_vec(v)
}
}
unsafe fn shared_v_is_unique(data: &AtomicPtr<()>) -> bool {
let shared = data.load(Ordering::Acquire);
let ref_count = (*shared.cast::<Shared>()).ref_count.load(Ordering::Relaxed);
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 -4
View File
@@ -114,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,
@@ -124,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
);
}
+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
+406 -66
View File
@@ -1,75 +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]
#[should_panic]
fn test_get_u16_buffer_underflow() {
let mut buf = &b"\x21"[..];
buf.get_u16();
mod bytes {
fn make_input(buf: &'static [u8]) -> impl Buf {
Bytes::from_static(buf)
}
buf_tests!(make_input);
}
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.
@@ -104,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]);
}
+642 -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);
@@ -323,6 +339,31 @@ fn truncate() {
assert_eq!(hello, "hello");
}
#[test]
fn truncate_to_zero_releases_shared_reference() {
let mut bytes = BytesMut::from(&b"hello"[..]);
drop(bytes.split_off(bytes.len()));
let mut truncated = bytes.freeze();
let remaining = truncated.clone();
truncated.truncate(0);
assert!(truncated.is_empty());
let mut remaining = remaining.try_into_mut().unwrap();
remaining[0] = b'H';
assert_eq!(remaining, b"Hello"[..]);
let mut bytes = BytesMut::from(&b"hello"[..]);
drop(bytes.split_off(bytes.len()));
let mut nonempty = bytes.freeze();
let remaining = nonempty.clone();
nonempty.truncate(1);
assert_eq!(nonempty, b"h"[..]);
assert!(remaining.try_into_mut().is_err());
}
#[test]
fn freeze_clone_shared() {
let s = &b"abcdefgh"[..];
@@ -591,6 +632,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);
@@ -676,6 +734,43 @@ fn advance_bytes_mut() {
assert_eq!(a, b"d zomg wat wat"[..]);
}
// Ensures BytesMut::advance reduces always capacity
//
// See https://github.com/tokio-rs/bytes/issues/725
#[test]
fn advance_bytes_mut_remaining_capacity() {
// reduce the search space under miri
let max_capacity = if cfg!(miri) { 16 } else { 256 };
for capacity in 0..=max_capacity {
for len in 0..=capacity {
for advance in 0..=len {
eprintln!("testing capacity={capacity}, len={len}, advance={advance}");
let mut buf = BytesMut::with_capacity(capacity);
buf.resize(len, 42);
assert_eq!(buf.len(), len, "resize should write `len` bytes");
assert_eq!(
buf.remaining(),
len,
"Buf::remaining() should equal BytesMut::len"
);
buf.advance(advance);
assert_eq!(
buf.remaining(),
len - advance,
"Buf::advance should reduce the remaining len"
);
assert_eq!(
buf.capacity(),
capacity - advance,
"Buf::advance should reduce the remaining capacity"
);
}
}
}
}
#[test]
#[should_panic]
fn advance_past_len() {
@@ -683,10 +778,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};
@@ -1181,3 +1286,538 @@ fn mut_shared_is_unique() {
drop(b);
assert!(c.is_unique());
}
#[test]
fn test_bytesmut_from_bytes_static() {
let bs = b"1b23exfcz3r";
// Test STATIC_VTABLE.to_mut
let bytes_mut = BytesMut::from(Bytes::from_static(bs));
assert_eq!(bytes_mut, bs[..]);
}
#[test]
fn test_bytesmut_from_bytes_bytes_mut_vec() {
let bs = b"1b23exfcz3r";
let bs_long = b"1b23exfcz3r1b23exfcz3r";
// Test case where kind == KIND_VEC
let mut bytes_mut: BytesMut = bs[..].into();
bytes_mut = BytesMut::from(bytes_mut.freeze());
assert_eq!(bytes_mut, bs[..]);
bytes_mut.extend_from_slice(&bs[..]);
assert_eq!(bytes_mut, bs_long[..]);
}
#[test]
fn test_bytesmut_from_bytes_bytes_mut_shared() {
let bs = b"1b23exfcz3r";
// Set kind to KIND_ARC so that after freeze, Bytes will use bytes_mut.SHARED_VTABLE
let mut bytes_mut: BytesMut = bs[..].into();
drop(bytes_mut.split_off(bs.len()));
let b1 = bytes_mut.freeze();
let b2 = b1.clone();
// shared.is_unique() = False
let mut b1m = BytesMut::from(b1);
assert_eq!(b1m, bs[..]);
b1m[0] = b'9';
// shared.is_unique() = True
let b2m = BytesMut::from(b2);
assert_eq!(b2m, bs[..]);
}
#[test]
fn test_bytesmut_from_bytes_bytes_mut_offset() {
let bs = b"1b23exfcz3r";
// Test bytes_mut.SHARED_VTABLE.to_mut 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();
let b1m = BytesMut::from(b1);
let b2m = BytesMut::from(b2);
assert_eq!(b2m, bs[9..]);
assert_eq!(b1m, bs[..9]);
}
#[test]
fn test_bytesmut_from_bytes_promotable_even_vec() {
let vec = vec![33u8; 1024];
// Test case where kind == KIND_VEC
let b1 = Bytes::from(vec.clone());
let b1m = BytesMut::from(b1);
assert_eq!(b1m, vec);
}
#[test]
fn test_bytesmut_from_bytes_promotable_even_arc_1() {
let vec = vec![33u8; 1024];
// Test case where kind == KIND_ARC, ref_cnt == 1
let b1 = Bytes::from(vec.clone());
drop(b1.clone());
let b1m = BytesMut::from(b1);
assert_eq!(b1m, vec);
}
#[test]
fn test_bytesmut_from_bytes_promotable_even_arc_2() {
let vec = vec![33u8; 1024];
// Test case where kind == KIND_ARC, ref_cnt == 2
let b1 = Bytes::from(vec.clone());
let b2 = b1.clone();
let b1m = BytesMut::from(b1);
assert_eq!(b1m, vec);
// Test case where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1
let b2m = BytesMut::from(b2);
assert_eq!(b2m, vec);
}
#[test]
fn test_bytesmut_from_bytes_promotable_even_arc_offset() {
let vec = vec![33u8; 1024];
// Test case where offset != 0
let mut b1 = Bytes::from(vec.clone());
let b2 = b1.split_off(20);
let b1m = BytesMut::from(b1);
let b2m = BytesMut::from(b2);
assert_eq!(b2m, vec[20..]);
assert_eq!(b1m, vec[..20]);
}
#[test]
fn try_reclaim_empty() {
let mut buf = BytesMut::new();
assert_eq!(false, buf.try_reclaim(6));
buf.reserve(6);
assert_eq!(true, buf.try_reclaim(6));
let cap = buf.capacity();
assert!(cap >= 6);
assert_eq!(false, buf.try_reclaim(cap + 1));
let mut buf = BytesMut::new();
buf.reserve(6);
let cap = buf.capacity();
assert!(cap >= 6);
let mut split = buf.split();
drop(buf);
assert_eq!(0, split.capacity());
assert_eq!(true, split.try_reclaim(6));
assert_eq!(false, split.try_reclaim(cap + 1));
}
#[test]
fn try_reclaim_vec() {
let mut buf = BytesMut::with_capacity(6);
buf.put_slice(b"abc");
// Reclaiming a ludicrous amount of space should calmly return false
assert_eq!(false, buf.try_reclaim(usize::MAX));
assert_eq!(false, buf.try_reclaim(6));
buf.advance(2);
assert_eq!(4, buf.capacity());
// We can reclaim 5 bytes, because the byte in the buffer can be moved to the front. 6 bytes
// cannot be reclaimed because there is already one byte stored
assert_eq!(false, buf.try_reclaim(6));
assert_eq!(true, buf.try_reclaim(5));
buf.advance(1);
assert_eq!(true, buf.try_reclaim(6));
assert_eq!(6, buf.capacity());
}
#[test]
fn try_reclaim_arc() {
let mut buf = BytesMut::with_capacity(6);
buf.put_slice(b"abc");
let x = buf.split().freeze();
buf.put_slice(b"def");
// Reclaiming a ludicrous amount of space should calmly return false
assert_eq!(false, buf.try_reclaim(usize::MAX));
let y = buf.split().freeze();
let z = y.clone();
assert_eq!(false, buf.try_reclaim(6));
drop(x);
drop(z);
assert_eq!(false, buf.try_reclaim(6));
drop(y);
assert_eq!(true, buf.try_reclaim(6));
assert_eq!(6, buf.capacity());
assert_eq!(0, buf.len());
buf.put_slice(b"abc");
buf.put_slice(b"def");
assert_eq!(6, buf.capacity());
assert_eq!(6, buf.len());
assert_eq!(false, buf.try_reclaim(6));
buf.advance(4);
assert_eq!(true, buf.try_reclaim(4));
buf.advance(2);
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]);
let ptr_start = buf.as_ptr();
let ptr_end = ptr_start.wrapping_add(1024);
let empty_end = buf.split_off(1024);
assert_eq!(empty_end.len(), 0);
assert_eq!(empty_end.as_ptr(), ptr_end);
let _ = buf.split_off(0);
assert_eq!(buf.len(), 0);
assert_eq!(buf.as_ptr(), ptr_start);
// Is miri happy about the provenance?
let _ = &empty_end[..];
let _ = &buf[..];
}
#[test]
fn split_to_empty_addr() {
let mut buf = Bytes::from(vec![0; 1024]);
let ptr_start = buf.as_ptr();
let ptr_end = ptr_start.wrapping_add(1024);
let empty_start = buf.split_to(0);
assert_eq!(empty_start.len(), 0);
assert_eq!(empty_start.as_ptr(), ptr_start);
let _ = buf.split_to(1024);
assert_eq!(buf.len(), 0);
assert_eq!(buf.as_ptr(), ptr_end);
// Is miri happy about the provenance?
let _ = &empty_start[..];
let _ = &buf[..];
}
#[test]
fn split_off_empty_addr_mut() {
let mut buf = BytesMut::from([0; 1024].as_slice());
let ptr_start = buf.as_ptr();
let ptr_end = ptr_start.wrapping_add(1024);
let empty_end = buf.split_off(1024);
assert_eq!(empty_end.len(), 0);
assert_eq!(empty_end.as_ptr(), ptr_end);
let _ = buf.split_off(0);
assert_eq!(buf.len(), 0);
assert_eq!(buf.as_ptr(), ptr_start);
// Is miri happy about the provenance?
let _ = &empty_end[..];
let _ = &buf[..];
}
#[test]
fn split_to_empty_addr_mut() {
let mut buf = BytesMut::from([0; 1024].as_slice());
let ptr_start = buf.as_ptr();
let ptr_end = ptr_start.wrapping_add(1024);
let empty_start = buf.split_to(0);
assert_eq!(empty_start.len(), 0);
assert_eq!(empty_start.as_ptr(), ptr_start);
let _ = buf.split_to(1024);
assert_eq!(buf.len(), 0);
assert_eq!(buf.as_ptr(), ptr_end);
// Is miri happy about the provenance?
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');
}
+51 -1
View File
@@ -6,7 +6,7 @@
use std::alloc::{GlobalAlloc, Layout, System};
use std::ptr;
use bytes::Bytes;
use bytes::{Bytes, BytesMut};
#[global_allocator]
static ODD: Odd = Odd;
@@ -95,3 +95,53 @@ fn test_bytes_into_vec() {
assert_eq!(Vec::from(b2), vec[20..]);
assert_eq!(Vec::from(b1), vec[..20]);
}
#[test]
fn test_bytesmut_from_bytes_vec() {
let vec = vec![33u8; 1024];
// Test case where kind == KIND_VEC
let b1 = Bytes::from(vec.clone());
let b1m = BytesMut::from(b1);
assert_eq!(b1m, vec);
}
#[test]
fn test_bytesmut_from_bytes_arc_1() {
let vec = vec![33u8; 1024];
// Test case where kind == KIND_ARC, ref_cnt == 1
let b1 = Bytes::from(vec.clone());
drop(b1.clone());
let b1m = BytesMut::from(b1);
assert_eq!(b1m, vec);
}
#[test]
fn test_bytesmut_from_bytes_arc_2() {
let vec = vec![33u8; 1024];
// Test case where kind == KIND_ARC, ref_cnt == 2
let b1 = Bytes::from(vec.clone());
let b2 = b1.clone();
let b1m = BytesMut::from(b1);
assert_eq!(b1m, vec);
// Test case where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1
let b2m = BytesMut::from(b2);
assert_eq!(b2m, vec);
}
#[test]
fn test_bytesmut_from_bytes_arc_offset() {
let vec = vec![33u8; 1024];
// Test case where offset != 0
let mut b1 = Bytes::from(vec.clone());
let b2 = b1.split_off(20);
let b1m = BytesMut::from(b1);
let b2m = BytesMut::from(b2);
assert_eq!(b2m, vec[20..]);
assert_eq!(b1m, vec[..20]);
}
+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
+4 -4
View File
@@ -1,11 +1,11 @@
#![warn(rust_2018_idioms)]
use bytes::Bytes;
use bytes::{buf::IntoIter, Bytes};
#[test]
fn iter_len() {
let buf = Bytes::from_static(b"hello world");
let iter = buf.iter();
let iter = IntoIter::new(buf);
assert_eq!(iter.size_hint(), (11, Some(11)));
assert_eq!(iter.len(), 11);
@@ -13,8 +13,8 @@ fn iter_len() {
#[test]
fn empty_iter_len() {
let buf = Bytes::from_static(b"");
let iter = buf.iter();
let buf = Bytes::new();
let iter = IntoIter::new(buf);
assert_eq!(iter.size_hint(), (0, Some(0)));
assert_eq!(iter.len(), 0);
+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]);
}
}