diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..00b02bb --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,90 @@ +name: CI + +on: + push: + branches: ["**"] + pull_request: + branches: ["**"] + +jobs: + check: + name: Check (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust toolchain + run: rustup update stable + + - name: Make sure necessary tools are installed + run: rustup component add clippy --toolchain stable + + - name: Setup rust-cache + uses: Swatinem/rust-cache@v2 + with: + cache-bin: false + + - name: Run cargo clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Check formatting (cargo fmt) + run: cargo fmt --all --check + + test: + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust toolchain + run: rustup update stable + + - name: Setup rust-cache + uses: Swatinem/rust-cache@v2 + with: + cache-bin: false + + - name: Run cargo test + run: cargo test --all-features + + toml: + name: Format (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust toolchain + run: rustup update stable + + - name: Setup rust-cache + uses: Swatinem/rust-cache@v2 + with: + cache-bin: false + + - name: Install latest cargo-machete + uses: taiki-e/install-action@cargo-machete + + - name: Install latest taplo + uses: taiki-e/install-action@taplo + + - name: Check unused dependencies (cargo machete) + run: cargo machete + + - name: Check TOML formatting (taplo) + if: ${{ !contains(matrix.os, 'windows') }} + run: taplo fmt --check --diff diff --git a/Cargo.toml b/Cargo.toml index ea3680d..402faa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" description = "A fast thread-safe single producer-single consumer ring buffer" repository = "https://github.com/Mallets/ringbuffer-spsc" license = "EPL-2.0 OR Apache-2.0" +rust-version = "1.79" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 0354847..6ae9cc1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +[![CI](https://github.com/Mallets/ringbuffer-spsc/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Mallets/ringbuffer-spsc/actions?query=workflow%3ACI+branch%3Amain++) +[![docs.rs](https://img.shields.io/docsrs/ringbuffer-spsc)](https://docs.rs/ringbuffer-spsc/latest/ringbuffer_spsc/) +[![License](https://img.shields.io/badge/License-EPL%202.0-blue)](https://choosealicense.com/licenses/epl-2.0/) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + # ringbuffer-spsc A fast single-producer single-consumer ring buffer. @@ -48,7 +53,7 @@ fn main() { Tests run on an Apple M4, 32 GB of RAM. ```sh -$ cargo run --release --example throughput +cargo run --release --example throughput ``` Provides `~520M elem/s` of sustained throughput. @@ -65,4 +70,4 @@ Provides `~520M elem/s` of sustained throughput. 520486274 elem/s 522570696 elem/s ... -``` \ No newline at end of file +``` diff --git a/src/lib.rs b/src/lib.rs index 5f4b907..185fa61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,10 @@ //! //! # Example //! ```rust -//! use ringbuffer_spsc::RingBuffer; +//! use ringbuffer_spsc::ringbuffer; //! //! const N: usize = 1_000_000; -//! let (mut tx, mut rx) = RingBuffer::::new(16); +//! let (mut tx, mut rx) = ringbuffer::(16); //! //! let p = std::thread::spawn(move || { //! let mut current: usize = 0; @@ -49,6 +49,7 @@ use crossbeam_utils::CachePadded; /// Panic: it panics if capacity is not a power of 2. pub fn ringbuffer(capacity: usize) -> (RingBufferWriter, RingBufferReader) { assert!(capacity.is_power_of_two(), "Capacity must be a power of 2"); + // Inner container let v = (0..capacity) .map(|_| MaybeUninit::uninit()) @@ -56,10 +57,10 @@ pub fn ringbuffer(capacity: usize) -> (RingBufferWriter, RingBufferReader< .into_boxed_slice(); let rb = Arc::new(RingBuffer { - // Keep + // Keep the pointer to the boxed slice ptr: Box::into_raw(v), // Since capacity is a power of two, capacity-1 is a mask covering N elements overflowing when N elements have been added. - // Indexes are left growing indefinetely and naturally wraps around once the index increment reaches usize::MAX. + // Indexes are left growing indefinetely and naturally wrap around once the index increment reaches usize::MAX. mask: capacity - 1, idx_r: CachePadded::new(AtomicUsize::new(0)), idx_w: CachePadded::new(AtomicUsize::new(0)), @@ -86,6 +87,7 @@ struct RingBuffer { } impl RingBuffer { + #[allow(clippy::mut_from_ref)] unsafe fn get_unchecked_mut(&self, idx: usize) -> &mut MaybeUninit { unsafe { (&mut (*self.ptr)).get_unchecked_mut(idx & self.mask) } }