Compare commits

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

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

* Always check overflow in new_cap + offset

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

---------

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

I opted for using an explicit include list instead of an exclude list to prevent these files from being included in the published packages to make sure that everything that's included is an conscious choice.
2025-12-17 13:51:19 +00:00
Alice RyhlandGitHub b4ed70daee Add test for copy_to_bytes() -> BytesMut avoiding clone (#809) 2025-12-02 13:08:19 +01:00
Paolo BarboliniandGitHub 94e42915a9 Document that BytesMut::{reserve,try_reserve} doesn't preserve unused capacity (#808) 2025-11-28 10:02:34 +01:00
Paolo BarboliniandGitHub acd1e0ffb8 Fix get_int if nbytes is zero (#806) 2025-11-21 11:14:40 +01:00
Martin GrigorovandGitHub a7952fb447 chore: prepare bytes v1.11.0 (#804) 2025-11-14 15:44:53 +01:00
discord9andGitHub 60cbb776f2 fix: BytesMut only reuse if src has remaining (#803)
Signed-off-by: discord9 <[email protected]>
2025-11-14 10:52:50 +01:00
Alice RyhlandGitHub 7ce330f519 Move drop_fn of from_owner into vtable (#801) 2025-11-10 15:16:58 +01:00
Stepan KoltsovandGitHub 4b53a29eb2 Tweak BytesMut::remaining_mut (#795) 2025-08-20 11:57:49 +02:00
Stepan KoltsovandGitHub 016fdbdc7a Reserve capacity in BytesMut::put (#794) 2025-08-19 12:50:38 +02:00
Stepan KoltsovandGitHub ef7f25736c Specialize BytesMut::put::<Bytes> (#793) 2025-08-14 08:55:18 +00:00
Taiki EndoandGitHub 8b4f54d0f3 Ignore BytesMut::freeze doctest on wasm (#790) 2025-08-10 19:34:43 +09:00
Paolo BarboliniandGitHub 16132ad259 Fix latest clippy warnings (#787) 2025-07-16 10:53:50 +02:00
Paolo BarboliniandGitHub 3e44f88f5f Bump MSRV to 1.57 (#788) 2025-07-16 10:33:07 +02:00
Hange Shi - NJUandGitHub f29e93951d Add some tests for Limit, BytesMut and Reader (#785) 2025-05-28 12:56:41 +02:00
Michael FärberandGitHub d9d5c59ef8 Guarantee address in slice() for empty slices. (#780) 2025-05-20 14:52:05 +02:00
AbeZbm 5c90b11415 Add missing tests in test_bytes 2025-05-15 14:52:54 +08:00
Scott LambandGitHub 141cbc22cb Rename Vtable::to_* -> Vtable::into_* (#776) 2025-03-07 13:58:07 +00:00
Alice RyhlandGitHub 19d1427c97 chore: prepare bytes v1.10.1 (#774) 2025-03-05 13:11:49 +01:00
venomousmoogandGitHub 36675436cc Fix memory leak in owned_to_vec (#773) 2025-03-05 10:44:40 +01:00
Alice RyhlandGitHub e0f3a25fae chore: prepare bytes v1.10.0 (#766) 2025-02-03 12:49:49 +01:00
Taiki EndoandGitHub 16fd473d5c Add feature to support platforms without atomic CAS (#467) 2025-01-31 18:52:54 +09:00
Taiki EndoandGitHub 71824b095c ci: test AArch64 and run tests instead of build in cross job (#763) 2025-01-27 10:58:12 +01:00
Taiki EndoandGitHub 7a876609a2 Use [lints] to address unexpected_cfgs lint (#762) 2025-01-27 10:56:32 +01:00
hpenneandGitHub 3ab876fee6 Try get methods for Buf trait (#753) 2025-01-21 13:41:42 +01:00
Alice RyhlandGitHub a1b1208127 Disable test_bytes_vec_alloc test on miri (#755) 2025-01-13 16:28:28 +01:00
aae4969fde Add specialized Buf::chunks_vectored for Take (#617)
Co-authored-by: Alice Ryhl <[email protected]>
Co-authored-by: Michal 'vorner' Vaner <[email protected]>
2025-01-13 12:53:52 +00:00
Alice RyhlandGitHub 103d7bf9e0 Remove incorrect guarantee for chunks_vectored (#754) 2025-01-10 23:13:52 +01:00
Paolo BarboliniandGitHub 16cc574fea Implement Buf::chunks_vectored for VecDeque<u8> (#708) 2025-01-10 14:40:12 +01:00
Paolo BarboliniandGitHub 0e9e4fc2f1 Expand Buf tests (#747) 2025-01-09 17:13:47 +01:00
Alice RyhlandGitHub f7072c9267 Run tests under panic=abort (#749) 2024-12-10 22:30:27 +01:00
Kyle BarronandGitHub d0a14deeb5 chore: prepare bytes v1.9.0 (#748) 2024-11-28 13:43:45 +01:00
Leonid LogvinovandGitHub 54f1c26f69 Rename hex_impl! to fmt_impl! and reuse it for fmt::Debug (#743) 2024-11-08 10:23:02 +01:00
Paolo BarboliniandGitHub 4cd8969e85 Replace BufMut::put with BufMut::put_slice in Writer impl (#745) 2024-11-01 18:22:07 +00:00
katelyn martinandGitHub 2d996a2b41 Fix typo in Buf::chunk() comment (#744) 2024-11-01 14:46:09 +00:00
Adam CimarostiandGitHub 30ee8e9cba Add Bytes::from_owner (#742) 2024-10-29 13:18:03 +01:00
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
Alice RyhlandGitHub fd13c7dcdb chore: prepare bytes v1.6.1 (#720) 2024-07-13 09:45:33 +02:00
Emily Crandall FleischmanandGitHub 6b4b0eda29 Fix Bytes::is_unique when created from shared BytesMut (#718)
The `is_unique` entry in the vtable for `Bytes` created from a shared
`BytesMut` just called the `shared_is_unique` function from the `bytes`
module. However, that function dereferences the `data` argument` as
`bytes::Shared`, but the actual underlying type is `bytes_mut::Shared`.
2024-07-13 01:09:46 +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
Brad DunbarandGitHub ce8d8a0a02 chore: prepare bytes v1.6.0 (#681) 2024-03-22 20:55:20 +01:00
Brad DunbarandGitHub 536db06f16 Use ManuallyDrop instead of mem::forget (#675) 2024-03-14 14:40:03 +01:00
Brad DunbarandGitHub ca004117f8 Remove commented tests for Bytes::unsplit (#677)
Bytes doesn't have an unsplit method anymore. We can always retrieve
these from git history if necessary.
2024-03-04 09:05:00 +01:00
Brad DunbarandGitHub 7968f6f83d Remove redundant reserve call (#674) 2024-03-04 09:04:40 +01:00
Brad DunbarandGitHub c5fae00c76 copy_to_bytes: Add panic section to docs (#676)
Fixes #454.
2024-03-03 22:59:30 +09:00
Brad DunbarandGitHub 99584cc10d Use Iterator from the prelude (#673)
CI is [failing][failure] due to unused_imports because Iterator is
already in the prelude. Removing it fixes things up.

[failure]: https://github.com/tokio-rs/bytes/actions/runs/8034858583/job/21946873895
2024-03-03 00:40:17 +09:00
Brad DunbarandGitHub 46289278f5 Refactor split_at/split_to (#663)
* set len a little more concisely
* inline set_end
* remove kind assertions
* remove a duplicate assertion
* remove redundant assertion and min
* rename set_start to advance_unchecked
2024-02-23 14:22:58 -08:00
Brad DunbarandGitHub 1bcd2129d1 get_vec_pos: use &self instead of &mut self (#670)
I can't see any reason that get_vec_pos needs a &mut self.
2024-02-06 17:34:30 -05:00
Brad DunbarandGitHub f586ffc525 set_vec_pos does not need a second parameter (#672)
The second argument to `set_vec_pos` always contains the value of
`self.data`. Let's just use `self.data` and remove the second parameter
altogether.
2024-02-06 11:03:37 -08:00
Brad DunbarandGitHub c6972d6132 Calculate original capacity only if necessary (#666)
We don't need the original capacity if the shared data is unique, so
let's not calculate it until after that check.
2024-02-06 10:46:49 -08:00
Brad DunbarandGitHub 47e83056f2 Use sub instead of offset (#668)
We're always subtracting here, and we already have a usize, so `sub`
seems like a more appropriate usage to me.
2024-02-06 10:41:44 -08:00
Brad DunbarandGitHub 8bcac21cb4 Restore commented tests (#665)
These seem to have been commented by accident in #298, and are still
passing.
2024-02-06 10:17:01 -08:00
Brad DunbarandGitHub d2e7abdb29 refactor: make parameter mut in From<Vec> (#667)
Instead of re-declaring `vec`, we can just use a mut parameter.
2024-01-31 09:41:23 -05:00
Brad DunbarandGitHub e24587dd61 Remove unreachable else branch (#661) 2024-01-28 15:30:30 +03:30
Brad DunbarandGitHub 9257a6ea08 Remove an unnecessary else branch (#662) 2024-01-28 14:20:56 +03:30
Brad DunbarandGitHub 0ba3b4c4cd Remove unnecessary namespace qualifier (#660) 2024-01-28 14:07:11 +03:30
Cyborus04andGitHub 0864aea970 add Bytes::is_unique (#643) 2024-01-19 23:59:30 +01:00
Brad DunbarandGitHub abb4a2e66c BytesMut: Assert alignment of Shared (#652)
Back in #362, an assertion was added to ensure that the alignment of
bytes::Shared is even so we can use the least significant bit as a flag.
bytes_mut::Shared uses the same technique but has no such assertion so
I've added one here.
2024-01-19 11:08:27 +01:00
Taiki EndoandGitHub 09214ba51b Update CI config (#650) 2024-01-08 01:11:02 +09:00
Taiki EndoandGitHub fbc64bcc67 Update loom to 0.7 (#651) 2024-01-08 01:10:48 +09:00
Alice RyhlandGitHub 3bf6583b5c readme: add security policy (#649) 2024-01-03 17:22:39 +01:00
Brad DunbarandGitHub dbbdb63d76 Use self. instead of Self:: (#642)
I was a little confused about these calls using `Self::` instead of
`self.` here. Is there a reason to use the former instead of the latter?
2023-12-28 14:20:13 +09:00
Luca BrunoandGitHub f73c6c8e85 Simplify UninitSlice::as_uninit_slice_mut() logic (#644)
This reworks `UninitSlice::as_uninit_slice_mut()` using equivalent
simpler logic.
2023-12-28 14:17:53 +09:00
Gabriel GollerandGitHub 72cbb92e0e docs: fix broken links (#639)
Fixed a few broken links and converted a lot of them from the
html-link to intra-doc links.
2023-11-16 06:24:21 -05:00
bde8c50703 docs: typo fix (#637)
Co-authored-by: Daniel Bauman <[email protected]>
2023-10-19 21:50:24 +02:00
Alice RyhlandGitHub fd9243f9e2 Various cleanup (#635) 2023-10-02 15:40:02 +02:00
mxsmandGitHub a4e16a552b docs: fix some spelling mistakes (#633) 2023-09-25 10:47:02 +02:00
Lucas KentandGitHub a14ef4617c Move comment to correct constant (#629) 2023-09-11 11:12:11 +02:00
Alice RyhlandGitHub bd9c164cb6 doc: fix changelog typo (#628) 2023-09-07 14:10:55 +02:00
Alice RyhlandGitHub 74e6e200fd chore: prepare bytes v1.5.0 (#627) 2023-09-07 10:52:15 +02:00
Michal NazarewiczandGitHub 64c4fa2867 Rename UninitSlice constructors for consistency with ReadBuf (#599)
tokio::io::ReadBuf uses names `new` and `uninit` for its constructors.
For consistency with that, rename recently introduced UninitSlice
constructors to match those names.
2023-06-20 10:19:56 +02:00
Taiki EndoandGitHub c7756c3e59 Fix CI failure (#616) 2023-06-05 01:04:59 +09:00
Michal NazarewiczandGitHub b29112ce44 Implement BufMut for &mut [MaybeUninit<u8>] (#597) 2023-02-10 10:28:24 +01:00
Michal NazarewiczandGitHub 99a27542a0 Add a safe way to create UninitSlice from slices (#598)
Introduce UninitSlice::from_slice and UninitSlice::from_uninit_slice
methods which safely create Uninit slice from provided slice of maybe
uninitialised or initialised memory.

In addition, add `From<&mut [T]>` implementations (for `T=u8` and
`T=MaybeUninit<u8>`) which do conversion from slice to UninitSlice.

Closes: #552
2023-02-09 17:24:23 +01:00
Paa Kojo SamanpaandGitHub 74b04c7aae Mark BytesMut::extend_from_slice as inline (#595)
This function can be hot in applications that do a lot of encoding. Ideally would do the same for `<BytesMut as BufMut>::put_slice` and `<BytesMut as BufMut::put_u8`.
2023-02-04 20:16:41 +01:00
Alice RyhlandGitHub 21ed332836 chore: prepare bytes v1.4.0 (#593) 2023-01-31 20:38:32 +01:00
brian m. carlsonandGitHub 05e9d5cab9 Avoid large reallocations when freezing BytesMut (#592)
When we freeze a BytesMut, we turn it into a Vec, and then convert that
to a Bytes.  Currently, this happen using Vec::into_boxed_slice, which
reallocates to a slice of the same length as the Vev if the length and
the capacity are not equal.  This can pose a performance problem if the
Vec is large or if this happens many times in a loop.

Instead, let's compare the length and capacity, and if they're the same,
continue to handle this using into_boxed_slice.  Otherwise, since we
have a type of vtable which can handle a separate capacity, the shared
vtable, let's turn our Vec into that kind of Bytes.  While this does not
avoid allocation altogether, it performs a fixed size allocation and
avoids any need to memcpy.
2023-01-31 20:04:22 +01:00
0xc0001a2040andGitHub f15bba3375 Document which functions require std (#591) 2023-01-31 11:42:28 +01:00
c93a94b974 Fix duplicate "the the" typos (#585)
Co-authored-by: Nicolae Mihalache <[email protected]>
2022-12-20 11:49:55 +01:00
Matthijs van OtterdijkandGitHub 050d65b2ce make IntoIter constructor public (#581) 2022-11-25 22:48:20 +01:00
Sean LynchandGitHub 9b227220df chore: prepare bytes v1.3.0 (#579) 2022-11-21 08:06:49 +01:00
Sean LynchandGitHub 8a8c41f5c2 Implement native-endian get and put functions for Buf and BufMut (#576)
Fixes #549
2022-11-14 10:33:36 +01:00
Sean LynchandGitHub 5c7b4317e0 Update nightly snapshot to the current one (#577) 2022-11-14 10:56:33 +09:00
Yotam OfekandGitHub 6e4b1f244b Rename and expose BytesMut::spare_capacity_mut (#572) 2022-10-04 12:23:07 +02:00
Adam ChalmersandGitHub a36f661354 docs: Bytes::new etc should return Self not Bytes (#568) 2022-08-24 13:30:21 +02:00
Alice RyhlandGitHub d1b5d4ceb1 Don't have important data in unused capacity when calling reserve (#563) 2022-08-12 12:22:43 +02:00
Alice RyhlandGitHub b7249149a9 chore: prepare bytes v1.2.1 (#561) 2022-07-30 11:35:26 +02:00
Matt SchulteandGitHub d6e1999d97 Fix reserve over allocating underlying buffer (#560)
Fixes calls to `reserve` when the underlying shared buffer was already
big enough to fit the requested capacity. Previously a new even larger
buffer was created anyways. This could eventually lead to an OOM
condition.
2022-07-30 11:16:32 +02:00
38 changed files with 5665 additions and 802 deletions
+78 -39
View File
@@ -7,11 +7,13 @@ on:
push:
branches:
- master
schedule:
- cron: '0 2 * * 0'
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
nightly: nightly-2021-11-05
nightly: nightly-2024-09-15
defaults:
run:
@@ -23,11 +25,11 @@ jobs:
name: rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update stable && rustup default stable
run: rustup update stable
- name: Check formatting
run: cargo fmt --all -- --check
run: cargo fmt --all --check
# TODO
# # Apply clippy lints
@@ -35,7 +37,7 @@ jobs:
# name: clippy
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: actions/checkout@v4
# - name: Apply clippy lints
# run: cargo clippy --all-features
@@ -48,11 +50,11 @@ jobs:
name: minrust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update 1.39.0 && rustup default 1.39.0
- uses: actions/checkout@v4
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: Check
run: . ci/test-stable.sh check
run: cargo hack check --feature-powerset --optional-deps --rust-version
# Stable
stable:
@@ -61,73 +63,109 @@ 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 }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
# --no-self-update is necessary because the windows environment cannot self-update rustup.exe.
run: rustup update stable --no-self-update && rustup default stable
run: rustup update stable --no-self-update
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: Test
run: . ci/test-stable.sh test
run: ci/test-stable.sh test
# Nightly
nightly:
name: nightly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: Test
run: . ci/test-stable.sh 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@v3
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update stable && rustup default stable
- name: cross build --target ${{ matrix.target }}
run: |
cargo install cross
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'
run: rustup update stable
- 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:
name: tsan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Install rust-src
run: rustup component add rust-src
- name: ASAN / TSAN
run: . ci/tsan.sh
run: ci/tsan.sh
miri:
name: miri
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Miri
run: ci/miri.sh
@@ -136,7 +174,7 @@ jobs:
name: loom
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update $nightly && rustup default $nightly
- name: Loom tests
@@ -152,12 +190,13 @@ jobs:
- minrust
- cross
- tsan
- miri
- loom
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update stable && rustup default stable
run: rustup update $nightly && rustup default $nightly
- name: Build documentation
run: cargo doc --no-deps --all-features
env:
+223
View File
@@ -1,3 +1,226 @@
# 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
the `Bytes` originates from a shared `BytesMut`. (#718)
# 1.6.0 (March 22, 2024)
### Added
- Add `Bytes::is_unique` (#643)
### Documented
- Fix changelog typo (#628)
- Fix some spelling mistakes (#633)
- Typo fix (#637)
- Fix broken links (#639)
- Add security policy (#649)
### Internal changes
- Move comment to correct constant (#629)
- Various cleanup (#635)
- Simplify `UninitSlice::as_uninit_slice_mut()` logic (#644)
- Use `self.` instead of `Self::` (#642)
- `BytesMut`: Assert alignment of `Shared` (#652)
- Remove unnecessary namespace qualifier (#660)
- Remove an unnecessary else branch (#662)
- Remove unreachable else branch (#661)
- make parameter mut in `From<Vec>` (#667)
- Restore commented tests (#665)
- Use `sub` instead of `offset` (#668)
- Calculate original capacity only if necessary (#666)
- `set_vec_pos` does not need a second parameter (#672)
- `get_vec_pos`: use `&self` instead of `&mut self` (#670)
- Refactor `split_at`/`split_to` (#663)
- Use `Iterator` from the prelude (#673)
- `copy_to_bytes`: Add panic section to docs (#676)
- Remove redundant reserve call (#674)
- Use `ManuallyDrop` instead of `mem::forget` (#675)
# 1.5.0 (September 7, 2023)
### Added
- Add `UninitSlice::{new,uninit}` (#598, #599)
- Implement `BufMut` for `&mut [MaybeUninit<u8>]` (#597)
### Changed
- Mark `BytesMut::extend_from_slice` as inline (#595)
# 1.4.0 (January 31, 2023)
### Added
- Make `IntoIter` constructor public (#581)
### Fixed
- Avoid large reallocations when freezing `BytesMut` (#592)
### Documented
- Document which functions require `std` (#591)
- Fix duplicate "the the" typos (#585)
# 1.3.0 (November 20, 2022)
### Added
- Rename and expose `BytesMut::spare_capacity_mut` (#572)
- Implement native-endian get and put functions for `Buf` and `BufMut` (#576)
### Fixed
- Don't have important data in unused capacity when calling reserve (#563)
### Documented
- `Bytes::new` etc should return `Self` not `Bytes` (#568)
# 1.2.1 (July 30, 2022)
### Fixed
- Fix unbounded memory growth when using `reserve` (#560)
# 1.2.0 (July 19, 2022)
### Added
+15 -3
View File
@@ -4,7 +4,9 @@ name = "bytes"
# When releasing to crates.io:
# - Update CHANGELOG.md.
# - Create "v1.x.y" git tag.
version = "1.2.0"
version = "1.12.1"
edition = "2021"
rust-version = "1.57"
license = "MIT"
authors = [
"Carl Lerche <[email protected]>",
@@ -15,7 +17,7 @@ repository = "https://github.com/tokio-rs/bytes"
readme = "README.md"
keywords = ["buffers", "zero-copy", "io"]
categories = ["network-programming", "data-structures"]
edition = "2018"
include = ["CHANGELOG.md", "LICENSE", "README.md", "SECURITY.md", "Cargo.toml", "src/**/*.rs", "tests/**/*.rs", "clippy.toml"]
[features]
default = ["std"]
@@ -23,12 +25,22 @@ 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"
[target.'cfg(loom)'.dev-dependencies]
loom = "0.5"
loom = "0.7"
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(loom)',
] }
+26
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,17 @@ 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
feature gates will not be shown. This requires a nightly toolchain:
```
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc
```
## License
This project is licensed under the [MIT license](LICENSE).
+9
View File
@@ -0,0 +1,9 @@
# Security Policy
Bytes is part of the Tokio project and uses the same security policy as [Tokio][tokio-security].
## Report a security issue
The process for reporting an issue is the same as for [Tokio][tokio-security]. This includes private reporting via security@tokio.rs.
[tokio-security]: https://github.com/tokio-rs/tokio/security/policy
+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 '*'
Regular → Executable
+5 -8
View File
@@ -4,10 +4,6 @@ set -ex
cmd="${1:-test}"
# Install cargo-hack for feature flag test
host=$(rustc -Vv | grep host | sed 's/host: //')
curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-$host.tar.gz | tar xzf - -C ~/.cargo/bin
# Run with each feature
# * --each-feature includes both default/no-default features
# * --optional-deps is needed for serde feature
@@ -15,14 +11,15 @@ cargo hack "${cmd}" --each-feature --optional-deps
# Run with all features
cargo "${cmd}" --all-features
cargo doc --no-deps --all-features
if [[ "${RUST_VERSION}" == "nightly"* ]]; then
# Check benchmarks
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
Regular → Executable
View File
+1 -1
View File
@@ -1 +1 @@
msrv = "1.39"
msrv = "1.57"
+1968 -75
View File
File diff suppressed because it is too large Load Diff
+601 -76
View File
File diff suppressed because it is too large Load Diff
+4 -6
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;
@@ -25,9 +25,7 @@ use std::io::IoSlice;
/// assert_eq!(full[..], b"hello world"[..]);
/// ```
///
/// [`Buf::chain`]: trait.Buf.html#method.chain
/// [`Buf`]: trait.Buf.html
/// [`BufMut`]: trait.BufMut.html
/// [`Buf::chain`]: Buf::chain
#[derive(Debug)]
pub struct Chain<T, U> {
a: T,
@@ -135,7 +133,7 @@ where
U: Buf,
{
fn remaining(&self) -> usize {
self.a.remaining().checked_add(self.b.remaining()).unwrap()
self.a.remaining().saturating_add(self.b.remaining())
}
fn chunk(&self) -> &[u8] {
@@ -171,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)
+1 -6
View File
@@ -2,8 +2,6 @@ use crate::Buf;
/// Iterator over the bytes contained by the buffer.
///
/// This struct is created by the [`iter`] method on [`Buf`].
///
/// # Examples
///
/// Basic usage:
@@ -19,9 +17,6 @@ use crate::Buf;
/// assert_eq!(iter.next(), Some(b'c'));
/// assert_eq!(iter.next(), None);
/// ```
///
/// [`iter`]: trait.Buf.html#method.iter
/// [`Buf`]: trait.Buf.html
#[derive(Debug)]
pub struct IntoIter<T> {
inner: T,
@@ -43,7 +38,7 @@ impl<T> IntoIter<T> {
/// assert_eq!(iter.next(), Some(b'c'));
/// assert_eq!(iter.next(), None);
/// ```
pub(crate) fn new(inner: T) -> IntoIter<T> {
pub fn new(inner: T) -> IntoIter<T> {
IntoIter { inner }
}
-2
View File
@@ -13,8 +13,6 @@
//! See [`Buf`] and [`BufMut`] for more details.
//!
//! [rope]: https://en.wikipedia.org/wiki/Rope_(data_structure)
//! [`Buf`]: trait.Buf.html
//! [`BufMut`]: trait.BufMut.html
mod buf_impl;
mod buf_mut;
+1 -1
View File
@@ -5,7 +5,7 @@ use std::{cmp, io};
/// A `Buf` adapter which implements `io::Read` for the inner value.
///
/// This struct is generally created by calling `reader()` on `Buf`. See
/// documentation of [`reader()`](trait.Buf.html#method.reader) for more
/// documentation of [`reader()`](Buf::reader) for more
/// details.
#[derive(Debug)]
pub struct Reader<B> {
+35 -3
View File
@@ -1,11 +1,14 @@
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
/// documentation of [`take()`](trait.Buf.html#method.take) for more details.
/// documentation of [`take()`](Buf::take) for more details.
#[derive(Debug)]
pub struct Take<T> {
inner: T,
@@ -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
}
}
+57 -9
View File
@@ -22,6 +22,44 @@ use core::ops::{
pub struct UninitSlice([MaybeUninit<u8>]);
impl UninitSlice {
/// Creates a `&mut UninitSlice` wrapping a slice of initialised memory.
///
/// # Examples
///
/// ```
/// use bytes::buf::UninitSlice;
///
/// let mut buffer = [0u8; 64];
/// let slice = UninitSlice::new(&mut buffer[..]);
/// ```
#[inline]
pub fn new(slice: &mut [u8]) -> &mut UninitSlice {
unsafe { &mut *(slice as *mut [u8] as *mut [MaybeUninit<u8>] as *mut UninitSlice) }
}
/// Creates a `&mut UninitSlice` wrapping a slice of uninitialised memory.
///
/// # Examples
///
/// ```
/// use bytes::buf::UninitSlice;
/// use core::mem::MaybeUninit;
///
/// let mut buffer = [MaybeUninit::uninit(); 64];
/// let slice = UninitSlice::uninit(&mut buffer[..]);
///
/// let mut vec = Vec::with_capacity(1024);
/// let spare: &mut UninitSlice = vec.spare_capacity_mut().into();
/// ```
#[inline]
pub fn uninit(slice: &mut [MaybeUninit<u8>]) -> &mut UninitSlice {
unsafe { &mut *(slice as *mut [MaybeUninit<u8>] as *mut UninitSlice) }
}
fn uninit_ref(slice: &[MaybeUninit<u8>]) -> &UninitSlice {
unsafe { &*(slice as *const [MaybeUninit<u8>] as *const UninitSlice) }
}
/// Create a `&mut UninitSlice` from a pointer and a length.
///
/// # Safety
@@ -44,7 +82,7 @@ impl UninitSlice {
pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut UninitSlice {
let maybe_init: &mut [MaybeUninit<u8>] =
core::slice::from_raw_parts_mut(ptr as *mut _, len);
&mut *(maybe_init as *mut [MaybeUninit<u8>] as *mut UninitSlice)
Self::uninit(maybe_init)
}
/// Write a single byte at the specified offset.
@@ -72,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`.
///
@@ -130,7 +168,7 @@ impl UninitSlice {
///
/// The caller **must not** read from the referenced memory and **must not** write
/// **uninitialized** bytes to the slice either. This is because `BufMut` implementation
/// that created the `UninitSlice` knows which parts are initialized. Writing uninitalized
/// that created the `UninitSlice` knows which parts are initialized. Writing uninitialized
/// bytes to the slice may cause the `BufMut` to read those bytes and trigger undefined
/// behavior.
///
@@ -146,8 +184,8 @@ impl UninitSlice {
/// };
/// ```
#[inline]
pub unsafe fn as_uninit_slice_mut<'a>(&'a mut self) -> &'a mut [MaybeUninit<u8>] {
&mut *(self as *mut _ as *mut [MaybeUninit<u8>])
pub unsafe fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit<u8>] {
&mut self.0
}
/// Returns the number of bytes in the slice.
@@ -175,6 +213,18 @@ impl fmt::Debug for UninitSlice {
}
}
impl<'a> From<&'a mut [u8]> for &'a mut UninitSlice {
fn from(slice: &'a mut [u8]) -> Self {
UninitSlice::new(slice)
}
}
impl<'a> From<&'a mut [MaybeUninit<u8>]> for &'a mut UninitSlice {
fn from(slice: &'a mut [MaybeUninit<u8>]) -> Self {
UninitSlice::uninit(slice)
}
}
macro_rules! impl_index {
($($t:ty),*) => {
$(
@@ -183,16 +233,14 @@ macro_rules! impl_index {
#[inline]
fn index(&self, index: $t) -> &UninitSlice {
let maybe_uninit: &[MaybeUninit<u8>] = &self.0[index];
unsafe { &*(maybe_uninit as *const [MaybeUninit<u8>] as *const UninitSlice) }
UninitSlice::uninit_ref(&self.0[index])
}
}
impl IndexMut<$t> for UninitSlice {
#[inline]
fn index_mut(&mut self, index: $t) -> &mut UninitSlice {
let maybe_uninit: &mut [MaybeUninit<u8>] = &mut self.0[index];
unsafe { &mut *(maybe_uninit as *mut [MaybeUninit<u8>] as *mut UninitSlice) }
UninitSlice::uninit(&mut self.0[index])
}
}
)*
+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);
}
+2 -2
View File
@@ -5,7 +5,7 @@ use std::{cmp, io};
/// A `BufMut` adapter which implements `io::Write` for the inner value.
///
/// This struct is generally created by calling `writer()` on `BufMut`. See
/// documentation of [`writer()`](trait.BufMut.html#method.writer) for more
/// documentation of [`writer()`](BufMut::writer) for more
/// details.
#[derive(Debug)]
pub struct Writer<B> {
@@ -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)
}
+522 -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.
///
@@ -32,7 +33,7 @@ use crate::Buf;
/// All `Bytes` implementations must fulfill the following requirements:
/// - They are cheaply cloneable and thereby shareable between an unlimited amount
/// of components, for example by modifying a reference count.
/// - Instances can be sliced to refer to a subset of the the original buffer.
/// - Instances can be sliced to refer to a subset of the original buffer.
///
/// ```
/// use bytes::Bytes;
@@ -63,15 +64,15 @@ use crate::Buf;
/// `Bytes` contains a vtable, which allows implementations of `Bytes` to define
/// how sharing/cloning is implemented in detail.
/// When `Bytes::clone()` is called, `Bytes` will call the vtable function for
/// cloning the backing storage in order to share it behind between multiple
/// `Bytes` instances.
/// cloning the backing storage in order to share it behind multiple `Bytes`
/// instances.
///
/// For `Bytes` implementations which refer to constant memory (e.g. created
/// via `Bytes::from_static()`) the cloning implementation will be a no-op.
///
/// For `Bytes` implementations which point to a reference counted shared storage
/// (e.g. an `Arc<[u8]>`), sharing will be implemented by increasing the
/// the reference count.
/// reference count.
///
/// Due to this mechanism, multiple `Bytes` instances may point to the same
/// shared memory region.
@@ -105,15 +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 {
@@ -131,15 +139,16 @@ impl Bytes {
/// ```
#[inline]
#[cfg(not(all(loom, test)))]
pub const fn new() -> Bytes {
pub const fn new() -> Self {
// Make it a named const to work around
// "unsizing casts are not allowed in const fn"
const EMPTY: &[u8] = &[];
Bytes::from_static(EMPTY)
}
/// Creates a new empty `Bytes`.
#[cfg(all(loom, test))]
pub fn new() -> Bytes {
pub fn new() -> Self {
const EMPTY: &[u8] = &[];
Bytes::from_static(EMPTY)
}
@@ -159,7 +168,7 @@ impl Bytes {
/// ```
#[inline]
#[cfg(not(all(loom, test)))]
pub const fn from_static(bytes: &'static [u8]) -> Bytes {
pub const fn from_static(bytes: &'static [u8]) -> Self {
Bytes {
ptr: bytes.as_ptr(),
len: bytes.len(),
@@ -168,8 +177,9 @@ impl Bytes {
}
}
/// Creates a new `Bytes` from a static slice.
#[cfg(all(loom, test))]
pub fn from_static(bytes: &'static [u8]) -> Bytes {
pub fn from_static(bytes: &'static [u8]) -> Self {
Bytes {
ptr: bytes.as_ptr(),
len: bytes.len(),
@@ -178,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
@@ -208,6 +319,30 @@ impl Bytes {
self.len == 0
}
/// 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](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).
/// # Examples
///
/// ```
/// use bytes::Bytes;
///
/// let a = Bytes::from(vec![1, 2, 3]);
/// assert!(a.is_unique());
/// let b = a.clone();
/// assert!(!a.is_unique());
/// ```
pub fn is_unique(&self) -> bool {
unsafe { (self.vtable.is_unique)(&self.data) }
}
/// Creates `Bytes` instance from slice, by copying it.
pub fn copy_from_slice(data: &[u8]) -> Self {
data.to_vec().into()
@@ -235,38 +370,11 @@ impl Bytes {
///
/// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing
/// will panic.
pub fn slice(&self, range: impl RangeBounds<usize>) -> Bytes {
use core::ops::Bound;
let len = self.len();
let begin = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n + 1,
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,
);
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
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();
@@ -302,7 +410,7 @@ impl Bytes {
///
/// Requires that the given `sub` slice is in fact contained within the
/// `Bytes` buffer; otherwise this function will panic.
pub fn slice_ref(&self, subset: &[u8]) -> Bytes {
pub fn slice_ref(&self, subset: &[u8]) -> Self {
// Empty slice and empty Bytes may have their pointers reset
// so explicitly allow empty slice to be a subslice of any slice.
if subset.is_empty() {
@@ -338,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.
@@ -359,7 +469,15 @@ 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) -> Bytes {
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: {:?} <= {:?}",
@@ -367,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
@@ -408,7 +520,16 @@ 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) -> Bytes {
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: {:?} <= {:?}",
@@ -416,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;
@@ -438,7 +553,7 @@ impl Bytes {
/// If `len` is greater than the buffer's current length, this has no
/// effect.
///
/// The [`split_off`] method can emulate `truncate`, but this causes the
/// The [split_off](`Self::split_off()`) method can emulate `truncate`, but this causes the
/// excess bytes to be returned instead of dropped.
///
/// # Examples
@@ -450,8 +565,6 @@ impl Bytes {
/// buf.truncate(5);
/// assert_eq!(buf, b"hello"[..]);
/// ```
///
/// [`split_off`]: #method.split_off
#[inline]
pub fn truncate(&mut self, len: usize) {
if len < self.len {
@@ -484,6 +597,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,
@@ -513,6 +652,11 @@ impl Bytes {
self.len -= by;
self.ptr = self.ptr.add(by);
}
#[inline]
fn data_mut(&mut self) -> *mut () {
self.data.with_mut(|p| *p)
}
}
// Vtable must enforce this behavior
@@ -522,7 +666,8 @@ unsafe impl Sync for Bytes {}
impl Drop for Bytes {
#[inline]
fn drop(&mut self) {
unsafe { (self.vtable.drop)(&mut self.data, self.ptr, self.len) }
let data = self.data_mut();
unsafe { (self.vtable.drop)(data, self.ptr, self.len) }
}
}
@@ -558,14 +703,8 @@ impl Buf for Bytes {
}
}
fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes {
if len == self.remaining() {
core::mem::replace(self, Bytes::new())
} else {
let ret = self.slice(..len);
self.advance(len);
ret
}
fn copy_to_bytes(&mut self, len: usize) -> Self {
self.split_to(len)
}
}
@@ -634,7 +773,7 @@ impl PartialEq for Bytes {
impl PartialOrd for Bytes {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
self.as_slice().partial_cmp(other.as_slice())
Some(self.cmp(other))
}
}
@@ -807,8 +946,38 @@ impl From<&'static str> for Bytes {
impl From<Vec<u8>> for Bytes {
fn from(vec: Vec<u8>) -> Bytes {
let slice = vec.into_boxed_slice();
slice.into()
// 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();
let shared = Shared::init_to_raw(
shared,
Shared {
buf: ptr,
cap,
ref_cnt: AtomicUsize::new(1),
},
);
// The pointer should be aligned, so this assert should
// always succeed.
debug_assert!(
0 == (shared as usize & KIND_MASK),
"internal: Box<Shared> should have an aligned pointer",
);
Bytes {
ptr,
len,
data: AtomicPtr::new(shared as _),
vtable: &SHARED_VTABLE,
}
}
}
@@ -843,6 +1012,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())
@@ -851,8 +1043,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) }
}
}
@@ -871,7 +1064,9 @@ 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,
};
@@ -880,26 +1075,109 @@ 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_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
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 (), _: *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,
};
@@ -917,12 +1195,11 @@ unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize
}
unsafe fn promotable_to_vec(
data: &AtomicPtr<()>,
shared: *mut (),
ptr: *const u8,
len: usize,
f: fn(*mut ()) -> *mut u8,
) -> Vec<u8> {
let shared = data.load(Ordering::Acquire);
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
@@ -933,7 +1210,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);
@@ -942,25 +1219,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 {
@@ -975,27 +1283,40 @@ 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 {
let shared = data.load(Ordering::Acquire);
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
let ref_cnt = (*shared.cast::<Shared>()).ref_cnt.load(Ordering::Relaxed);
ref_cnt == 1
} else {
true
}
}
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())
}
@@ -1008,6 +1329,15 @@ struct Shared {
ref_cnt: AtomicUsize,
}
impl Shared {
fn init_to_raw(b: Box<MaybeUninit<Self>>, v: Self) -> *mut Self {
let shared = Box::into_raw(b).cast::<Self>();
// SAFETY: The Box has the right layout.
unsafe { shared.write(v) };
shared
}
}
impl Drop for Shared {
fn drop(&mut self) {
unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) }
@@ -1022,7 +1352,9 @@ const _: [(); 0 - mem::align_of::<Shared>() % 2] = []; // Assert that the alignm
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,
};
@@ -1047,11 +1379,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);
@@ -1064,14 +1396,57 @@ 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_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
data.with_mut(|shared| {
release_shared(shared.cast());
});
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 {
let shared = data.load(Ordering::Acquire);
let ref_cnt = (*shared.cast::<Shared>()).ref_cnt.load(Ordering::Relaxed);
ref_cnt == 1
}
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 {
@@ -1097,7 +1472,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.
@@ -1108,16 +1483,18 @@ unsafe fn shallow_clone_vec(
// updated and since the buffer hasn't been promoted to an
// `Arc`, those three fields still are the components of the
// vector.
let shared = Box::new(Shared {
buf,
cap: (offset 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.
@@ -1137,7 +1514,7 @@ unsafe fn shallow_clone_vec(
// pointed to by `actual` will be visible.
match atom.compare_exchange(ptr as _, shared as _, Ordering::AcqRel, Ordering::Acquire) {
Ok(actual) => {
debug_assert!(actual as usize == ptr as usize);
debug_assert!(core::ptr::eq(actual, ptr));
// The upgrade was successful, the new handle can be
// returned.
Bytes {
@@ -1220,6 +1597,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
+467 -209
View File
File diff suppressed because it is too large Load Diff
+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;
+111 -8
View File
@@ -4,16 +4,14 @@
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
//! Provides abstractions for working with bytes.
//!
//! The `bytes` crate provides an efficient byte buffer structure
//! ([`Bytes`](struct.Bytes.html)) and traits for working with buffer
//! ([`Bytes`]) and traits for working with buffer
//! implementations ([`Buf`], [`BufMut`]).
//!
//! [`Buf`]: trait.Buf.html
//! [`BufMut`]: trait.BufMut.html
//!
//! # `Bytes`
//!
//! `Bytes` is an efficient container for storing and operating on contiguous
@@ -51,9 +49,7 @@
//! `a` and `b` will share the underlying buffer and maintain indices tracking
//! the view into the buffer represented by the handle.
//!
//! See the [struct docs] for more details.
//!
//! [struct docs]: struct.Bytes.html
//! See the [struct docs](`Bytes`) for more details.
//!
//! # `Buf`, `BufMut`
//!
@@ -69,7 +65,7 @@
//! ## Relation with `Read` and `Write`
//!
//! At first glance, it may seem that `Buf` and `BufMut` overlap in
//! functionality with `std::io::Read` and `std::io::Write`. However, they
//! functionality with [`std::io::Read`] and [`std::io::Write`]. However, they
//! serve different purposes. A buffer is the value that is provided as an
//! argument to `Read::read` and `Write::write`. `Read` and `Write` may then
//! perform a syscall, which has the potential of failing. Operations on `Buf`
@@ -114,3 +110,110 @@ fn abort() -> ! {
panic!("abort");
}
}
#[inline(always)]
#[cfg(feature = "std")]
fn saturating_sub_usize_u64(a: usize, b: u64) -> usize {
match usize::try_from(b) {
Ok(b) => a.saturating_sub(b),
Err(_) => 0,
}
}
#[inline(always)]
#[cfg(feature = "std")]
fn min_u64_usize(a: u64, b: usize) -> usize {
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(error_info: &TryGetError) -> ! {
panic!(
"advance out of bounds: the len is {} but advancing by {}",
error_info.available, error_info.requested
);
}
#[cold]
fn panic_does_not_fit(size: usize, nbytes: usize) -> ! {
panic!(
"size too large: the integer type can fit {} bytes, but nbytes is {}",
size, nbytes
);
}
+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
+407 -66
View File
@@ -1,77 +1,424 @@
#![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.
#[test]
fn test_deref_buf_forwards() {
struct Special;
@@ -103,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);
}
+122 -16
View File
@@ -3,7 +3,7 @@
use bytes::buf::UninitSlice;
use bytes::{BufMut, BytesMut};
use core::fmt::Write;
use core::usize;
use core::mem::MaybeUninit;
#[test]
fn test_vec_as_mut_buf() {
@@ -82,7 +82,7 @@ fn test_put_int_le_nbytes_overflow() {
}
#[test]
#[should_panic(expected = "cannot advance")]
#[should_panic(expected = "advance out of bounds: the len is 8 but advancing by 12")]
fn test_vec_advance_mut() {
// Verify fix for #354
let mut buf = Vec::with_capacity(8);
@@ -101,26 +101,123 @@ fn test_clone() {
assert!(buf != buf2);
}
#[test]
fn test_mut_slice() {
let mut v = vec![0, 0, 0, 0];
let mut s = &mut v[..];
s.put_u32(42);
fn do_test_slice_small<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
let mut buf = [b'X'; 8];
assert_eq!(s.len(), 0);
assert_eq!(&v, &[0, 0, 0, 42]);
let mut slice = make(&mut buf[..]);
slice.put_bytes(b'A', 2);
slice.put_u8(b'B');
slice.put_slice(b"BCC");
assert_eq!(2, slice.remaining_mut());
assert_eq!(b"AABBCCXX", &buf[..]);
let mut slice = make(&mut buf[..]);
slice.put_u32(0x61626364);
assert_eq!(4, slice.remaining_mut());
assert_eq!(b"abcdCCXX", &buf[..]);
let mut slice = make(&mut buf[..]);
slice.put_u32_le(0x30313233);
assert_eq!(4, slice.remaining_mut());
assert_eq!(b"3210CCXX", &buf[..]);
}
fn do_test_slice_large<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
const LEN: usize = 100;
const FILL: [u8; LEN] = [b'Y'; LEN];
let test = |fill: &dyn Fn(&mut &mut T, usize)| {
for buf_len in 0..LEN {
let mut buf = [b'X'; LEN];
for fill_len in 0..=buf_len {
let mut slice = make(&mut buf[..buf_len]);
fill(&mut slice, fill_len);
assert_eq!(buf_len - fill_len, slice.remaining_mut());
let (head, tail) = buf.split_at(fill_len);
assert_eq!(&FILL[..fill_len], head);
assert!(tail.iter().all(|b| *b == b'X'));
}
}
};
test(&|slice, fill_len| slice.put_slice(&FILL[..fill_len]));
test(&|slice, fill_len| slice.put_bytes(FILL[0], fill_len));
}
fn do_test_slice_put_slice_panics<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
let mut buf = [b'X'; 4];
let mut slice = make(&mut buf[..]);
slice.put_slice(b"12345");
}
fn do_test_slice_put_bytes_panics<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
let mut buf = [b'X'; 4];
let mut slice = make(&mut buf[..]);
slice.put_bytes(b'1', 5);
}
#[test]
fn test_slice_put_bytes() {
let mut v = [0, 0, 0, 0];
let mut s = &mut v[..];
s.put_u8(17);
s.put_bytes(19, 2);
assert_eq!(1, s.remaining_mut());
assert_eq!(&[17, 19, 19, 0], &v[..]);
fn test_slice_buf_mut_small() {
do_test_slice_small(|x| x);
}
#[test]
fn test_slice_buf_mut_large() {
do_test_slice_large(|x| x);
}
#[test]
#[should_panic]
fn test_slice_buf_mut_put_slice_overflow() {
do_test_slice_put_slice_panics(|x| x);
}
#[test]
#[should_panic]
fn test_slice_buf_mut_put_bytes_overflow() {
do_test_slice_put_bytes_panics(|x| x);
}
fn make_maybe_uninit_slice(slice: &mut [u8]) -> &mut [MaybeUninit<u8>] {
// SAFETY: [u8] has the same layout as [MaybeUninit<u8>].
unsafe { core::mem::transmute(slice) }
}
#[test]
fn test_maybe_uninit_buf_mut_small() {
do_test_slice_small(make_maybe_uninit_slice);
}
#[test]
fn test_maybe_uninit_buf_mut_large() {
do_test_slice_large(make_maybe_uninit_slice);
}
#[test]
#[should_panic]
fn test_maybe_uninit_buf_mut_put_slice_overflow() {
do_test_slice_put_slice_panics(make_maybe_uninit_slice);
}
#[test]
#[should_panic]
fn test_maybe_uninit_buf_mut_put_bytes_overflow() {
do_test_slice_put_bytes_panics(make_maybe_uninit_slice);
}
#[allow(unused_allocation)] // This is intentional.
#[test]
fn test_deref_bufmut_forwards() {
struct Special;
@@ -176,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]);
}
+754 -93
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);
@@ -515,6 +531,34 @@ fn reserve_in_arc_unique_doubles() {
assert_eq!(2000, bytes.capacity());
}
#[test]
fn reserve_in_arc_unique_does_not_overallocate_after_split() {
let mut bytes = BytesMut::from(LONG);
let orig_capacity = bytes.capacity();
drop(bytes.split_off(LONG.len() / 2));
// now bytes is Arc and refcount == 1
let new_capacity = bytes.capacity();
bytes.reserve(orig_capacity - new_capacity);
assert_eq!(bytes.capacity(), orig_capacity);
}
#[test]
fn reserve_in_arc_unique_does_not_overallocate_after_multiple_splits() {
let mut bytes = BytesMut::from(LONG);
let orig_capacity = bytes.capacity();
for _ in 0..10 {
drop(bytes.split_off(LONG.len() / 2));
// now bytes is Arc and refcount == 1
let new_capacity = bytes.capacity();
bytes.reserve(orig_capacity - new_capacity);
}
assert_eq!(bytes.capacity(), orig_capacity);
}
#[test]
fn reserve_in_arc_nonunique_does_not_overallocate() {
let mut bytes = BytesMut::with_capacity(1000);
@@ -563,6 +607,23 @@ fn extend_from_slice_mut() {
}
}
#[test]
fn extend_from_within_normal() {
let mut bytes = BytesMut::new();
bytes.extend_from_slice(&LONG[..23]);
bytes.extend_from_within(10..22);
bytes.extend_from_within(22..35);
assert_eq!(LONG[..], *bytes);
}
#[test]
#[should_panic]
fn extend_from_within_out_of_range() {
let mut bytes = BytesMut::new();
bytes.extend_from_slice(&LONG[..23]);
bytes.extend_from_within(23..=23);
}
#[test]
fn extend_mut_from_bytes() {
let mut bytes = BytesMut::with_capacity(0);
@@ -570,6 +631,28 @@ fn extend_mut_from_bytes() {
assert_eq!(*bytes, LONG[..]);
}
#[test]
fn extend_past_lower_limit_of_size_hint() {
// See https://github.com/tokio-rs/bytes/pull/674#pullrequestreview-1913035700
struct Iter<I>(I);
impl<I: Iterator<Item = u8>> Iterator for Iter<I> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(5, None)
}
}
let mut bytes = BytesMut::with_capacity(5);
bytes.extend(Iter(std::iter::repeat(0).take(10)));
assert_eq!(bytes.len(), 10);
}
#[test]
fn extend_mut_without_size_hint() {
let mut bytes = BytesMut::with_capacity(0);
@@ -626,6 +709,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() {
@@ -633,10 +753,20 @@ fn advance_past_len() {
a.advance(20);
}
#[test]
#[should_panic]
fn mut_advance_past_len() {
let mut a = BytesMut::from("hello world");
unsafe {
a.advance_mut(20);
}
}
#[test]
// Only run these tests on little endian systems. CI uses qemu for testing
// big endian... and qemu doesn't really support threading all that well.
#[cfg(any(miri, target_endian = "little"))]
#[cfg(not(target_family = "wasm"))] // wasm without experimental threads proposal doesn't support threads
fn stress() {
// Tests promoting a buffer from a vec -> shared in a concurrent situation
use std::sync::{Arc, Barrier};
@@ -682,97 +812,6 @@ fn partial_eq_bytesmut() {
assert!(bytesmut != bytes2);
}
/*
#[test]
fn bytes_unsplit_basic() {
let buf = Bytes::from(&b"aaabbbcccddd"[..]);
let splitted = buf.split_off(6);
assert_eq!(b"aaabbb", &buf[..]);
assert_eq!(b"cccddd", &splitted[..]);
buf.unsplit(splitted);
assert_eq!(b"aaabbbcccddd", &buf[..]);
}
#[test]
fn bytes_unsplit_empty_other() {
let buf = Bytes::from(&b"aaabbbcccddd"[..]);
// empty other
let other = Bytes::new();
buf.unsplit(other);
assert_eq!(b"aaabbbcccddd", &buf[..]);
}
#[test]
fn bytes_unsplit_empty_self() {
// empty self
let mut buf = Bytes::new();
let mut other = Bytes::with_capacity(64);
other.extend_from_slice(b"aaabbbcccddd");
buf.unsplit(other);
assert_eq!(b"aaabbbcccddd", &buf[..]);
}
#[test]
fn bytes_unsplit_arc_different() {
let mut buf = Bytes::with_capacity(64);
buf.extend_from_slice(b"aaaabbbbeeee");
buf.split_off(8); //arc
let mut buf2 = Bytes::with_capacity(64);
buf2.extend_from_slice(b"ccccddddeeee");
buf2.split_off(8); //arc
buf.unsplit(buf2);
assert_eq!(b"aaaabbbbccccdddd", &buf[..]);
}
#[test]
fn bytes_unsplit_arc_non_contiguous() {
let mut buf = Bytes::with_capacity(64);
buf.extend_from_slice(b"aaaabbbbeeeeccccdddd");
let mut buf2 = buf.split_off(8); //arc
let buf3 = buf2.split_off(4); //arc
buf.unsplit(buf3);
assert_eq!(b"aaaabbbbccccdddd", &buf[..]);
}
#[test]
fn bytes_unsplit_two_split_offs() {
let mut buf = Bytes::with_capacity(64);
buf.extend_from_slice(b"aaaabbbbccccdddd");
let mut buf2 = buf.split_off(8); //arc
let buf3 = buf2.split_off(4); //arc
buf2.unsplit(buf3);
buf.unsplit(buf2);
assert_eq!(b"aaaabbbbccccdddd", &buf[..]);
}
#[test]
fn bytes_unsplit_overlapping_references() {
let mut buf = Bytes::with_capacity(64);
buf.extend_from_slice(b"abcdefghijklmnopqrstuvwxyz");
let mut buf0010 = buf.slice(0..10);
let buf1020 = buf.slice(10..20);
let buf0515 = buf.slice(5..15);
buf0010.unsplit(buf1020);
assert_eq!(b"abcdefghijklmnopqrst", &buf0010[..]);
assert_eq!(b"fghijklmno", &buf0515[..]);
}
*/
#[test]
fn bytes_mut_unsplit_basic() {
let mut buf = BytesMut::with_capacity(64);
@@ -1135,3 +1174,625 @@ fn test_bytes_into_vec_promotable_even() {
assert_eq!(Vec::from(b2), vec[20..]);
assert_eq!(Vec::from(b1), vec[..20]);
}
#[test]
fn test_bytes_vec_conversion() {
let mut vec = Vec::with_capacity(10);
vec.extend(b"abcdefg");
let b = Bytes::from(vec);
let v = Vec::from(b);
assert_eq!(v.len(), 7);
assert_eq!(v.capacity(), 10);
let mut b = Bytes::from(v);
b.advance(1);
let v = Vec::from(b);
assert_eq!(v.len(), 6);
assert_eq!(v.capacity(), 10);
assert_eq!(v.as_slice(), b"bcdefg");
}
#[test]
fn test_bytes_mut_conversion() {
let mut b1 = BytesMut::with_capacity(10);
b1.extend(b"abcdefg");
let b2 = Bytes::from(b1);
let v = Vec::from(b2);
assert_eq!(v.len(), 7);
assert_eq!(v.capacity(), 10);
let mut b = Bytes::from(v);
b.advance(1);
let v = Vec::from(b);
assert_eq!(v.len(), 6);
assert_eq!(v.capacity(), 10);
assert_eq!(v.as_slice(), b"bcdefg");
}
#[test]
fn test_bytes_capacity_len() {
for cap in 0..100 {
for len in 0..=cap {
let mut v = Vec::with_capacity(cap);
v.resize(len, 0);
let _ = Bytes::from(v);
}
}
}
#[test]
fn static_is_unique() {
let b = Bytes::from_static(LONG);
assert!(!b.is_unique());
}
#[test]
fn vec_is_unique() {
let v: Vec<u8> = LONG.to_vec();
let b = Bytes::from(v);
assert!(b.is_unique());
}
#[test]
fn arc_is_unique() {
let v: Vec<u8> = LONG.to_vec();
let b = Bytes::from(v);
let c = b.clone();
assert!(!b.is_unique());
drop(c);
assert!(b.is_unique());
}
#[test]
fn shared_is_unique() {
let v: Vec<u8> = LONG.to_vec();
let b = Bytes::from(v);
let c = b.clone();
assert!(!c.is_unique());
drop(b);
assert!(c.is_unique());
}
#[test]
fn mut_shared_is_unique() {
let mut b = BytesMut::from(LONG);
let c = b.split().freeze();
assert!(!c.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]);
}
}