diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6973ccd55..79b57dafd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,6 +127,25 @@ jobs: - name: Test run: cargo test --target ${{ matrix.target }} + # Build for no_std environment. + no-std: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - name: Install 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 diff --git a/Cargo.toml b/Cargo.toml index 7dfc48e07..39def561e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,11 @@ std = [] [dependencies] serde = { version = "1.0.60", optional = true, default-features = false, features = ["alloc"] } +# Use portable-atomic crate to support platforms without atomic CAS. +# See "no_std support" section in readme for more information. +# +# Enable require-cas feature to provide a better error message if the end user forgets to use the cfg or feature. +extra-platforms = { package = "portable-atomic", version = "1.3", optional = true, default-features = false, features = ["require-cas"] } [dev-dependencies] serde_test = "1.0" diff --git a/README.md b/README.md index be46642a4..90d631c97 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,21 @@ Next, add this to your crate: use bytes::{Bytes, BytesMut, Buf, BufMut}; ``` +## no_std support + +To use `bytes` with no_std environment, disable the (enabled by default) `std` feature. + +```toml +[dependencies] +bytes = { version = "1", default-features = false } +``` + +To use `bytes` with no_std environment without atomic CAS, such as thumbv6m, you also need to enable +the `extra-platforms` feature. See the [documentation for the `portable-atomic` +crate](https://docs.rs/portable-atomic) for more information. + +The MSRV when `extra-platforms` feature is enabled depends on the MSRV of `portable-atomic`. + ## Serde support Serde support is optional and disabled by default. To enable use the feature `serde`. @@ -36,6 +51,8 @@ Serde support is optional and disabled by default. To enable use the feature `se bytes = { version = "1", features = ["serde"] } ``` +The MSRV when `serde` feature is enabled depends on the MSRV of `serde`. + ## Building documentation When building the `bytes` documentation the `docsrs` option should be used, otherwise diff --git a/ci/test-stable.sh b/ci/test-stable.sh index a8eaa3c65..ad9757449 100755 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -16,7 +16,10 @@ if [[ "${RUST_VERSION}" == "nightly"* ]]; then cargo check --benches # Check minimal versions - cargo clean - cargo update -Zminimal-versions + # Remove dev-dependencies from Cargo.toml to prevent the next `cargo update` + # from determining minimal versions based on dev-dependencies. + cargo hack --remove-dev-deps --workspace + # Update Cargo.lock to minimal version dependencies. + cargo update -Z minimal-versions cargo check --all-features fi diff --git a/src/buf/chain.rs b/src/buf/chain.rs index 97ac2eca5..c8bc36de9 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -1,5 +1,5 @@ use crate::buf::{IntoIter, UninitSlice}; -use crate::{Buf, BufMut, Bytes}; +use crate::{Buf, BufMut}; #[cfg(feature = "std")] use std::io::IoSlice; @@ -169,7 +169,7 @@ where n } - fn copy_to_bytes(&mut self, len: usize) -> Bytes { + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { let a_rem = self.a.remaining(); if a_rem >= len { self.a.copy_to_bytes(len) diff --git a/src/buf/take.rs b/src/buf/take.rs index fc4e39dda..acfeef6e1 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -1,4 +1,4 @@ -use crate::{Buf, Bytes}; +use crate::Buf; use core::cmp; @@ -148,7 +148,7 @@ impl Buf for Take { 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); diff --git a/src/loom.rs b/src/loom.rs index 9e6b2d5e2..c80929092 100644 --- a/src/loom.rs +++ b/src/loom.rs @@ -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 { fn with_mut(&mut self, f: F) -> R