circular-buffer-1.0.0/.cargo_vcs_info.json0000644000000001610000000000100141030ustar { "git": { "sha1": "e4abaa28fed2000af9c0a819140fde058d19f8c8", "dirty": true }, "path_in_vcs": "" }circular-buffer-1.0.0/.gitignore000064400000000000000000000000231046102023000146600ustar 00000000000000/target Cargo.lock circular-buffer-1.0.0/CHANGELOG.md000064400000000000000000000136441046102023000145160ustar 00000000000000# Changelog ## circular-buffer 1.0.0 ### New features * When the buffer is full, `push_front()` and `push_back()` now return the replaced element ([contributed by Zacchary Dempsey-Plante](https://github.com/andreacorbellini/rust-circular-buffer/pull/3)). ### Other changes * Increased the minimum supported rustc version to 1.82 ## circular-buffer 0.1.9 * This release does not introduce any new features or bug fixes. It only changes the dev-dependencies used by unit tests ([contributed by Ben Beasley](https://github.com/andreacorbellini/rust-circular-buffer/pull/16)). ## circular-buffer 0.1.8 ### New features * Added optional support for [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) ([contributed by DaneSlattery](https://github.com/andreacorbellini/rust-circular-buffer/pull/15)). ### Other changes * Renamed the `use_std` cargo feature to `std` (the old `use_std` is now an alias for `std`, so this is not a breaking change). ## circular-buffer 0.1.7 ### New features * Implemented the traits [`Index`](https://doc.rust-lang.org/std/ops/trait.Index.html) and [`IndexMut`](https://doc.rust-lang.org/std/ops/trait.IndexMut.html) for `CircularBuffer`. Now elements of the buffer can be accessed and modified with indexing operations (`buf[index]`), like in the following example: ```rust use circular_buffer::CircularBuffer; let mut buf = CircularBuffer::<5, char>::from(['a', 'b', 'c']); assert_eq!(buf[0], 'a'); buf[1] = 'd'; assert_eq!(buf[1], 'd'); ``` * Added new methods to fill the whole buffer, or the spare capacity of the buffer: [`fill()`](https://docs.rs/circular-buffer/0.1.7/circular_buffer/struct.CircularBuffer.html#method.fill), [`fill_with()`](https://docs.rs/circular-buffer/0.1.7/circular_buffer/struct.CircularBuffer.html#method.fill_with), [`fill_spare()`](https://docs.rs/circular-buffer/0.1.7/circular_buffer/struct.CircularBuffer.html#method.fill_spare), [`fill_spare_with()`](https://docs.rs/circular-buffer/0.1.7/circular_buffer/struct.CircularBuffer.html#method.fill_spare_with). * Added a new `alloc` feature that brings heap-allocation features to `no_std` environments through the [`alloc`](https://doc.rust-lang.org/stable/alloc/) crate ([contributed by Haoud](https://github.com/andreacorbellini/rust-circular-buffer/pull/11)). * Implemented the [`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html) trait for `CircularBuffer` (not available in `no_std` environments). ### Bug fixes * Fixed an out-of-bounds read in [`remove()`](https://docs.rs/circular-buffer/0.1.7/circular_buffer/struct.CircularBuffer.html#method.remove). * Removed `#[must_use]` from [`drain()`](https://docs.rs/circular-buffer/0.1.7/circular_buffer/struct.CircularBuffer.html#method.drain): it is perfectly acceptable to ignore the return value from this method. ### Other changes * Raised the minimum rustc version to 1.65 ## circular-buffer 0.1.6 * Fixed a bug in bug in bug in the [`PartialEq`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) implementation that would lead to a panic in some circumstances. ## circular-buffer 0.1.5 * Added [`try_push_back()`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.CircularBuffer.html#method.try_push_back) and [`try_push_front()`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.CircularBuffer.html#method.try_push_front) as non-overwriting alternatives to [`push_back()`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.CircularBuffer.html#method.push_back) and [`push_front()`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.CircularBuffer.html#method.push_front) ([contributed by Rinat Shigapov in GH-5](https://github.com/andreacorbellini/rust-circular-buffer/pull/5)). * Added [`drain()`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.CircularBuffer.html#method.drain) to remove ranges of elements. * Added [`make_contiguous()`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.CircularBuffer.html#method.make_contiguous) to return a contiguous mutable slice of elements. * [`Iter`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.Iter.html) and [`IterMut`](https://docs.rs/circular-buffer/0.1.5/circular_buffer/struct.IterMut.html) now implement the [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait. ## circular-buffer 0.1.4 * Fixed a bug in [`range()`](https://docs.rs/circular-buffer/0.1.4/circular_buffer/struct.CircularBuffer.html#method.range) and [`range_mut()`](https://docs.rs/circular-buffer/0.1.4/circular_buffer/struct.CircularBuffer.html#method.range_mut) that made them return more elements than requested in some circumstances. ## circular-buffer 0.1.3 * Fixed [`range()`](https://docs.rs/circular-buffer/0.1.3/circular_buffer/struct.CircularBuffer.html#method.range) and [`range_mut()`](https://docs.rs/circular-buffer/0.1.3/circular_buffer/struct.CircularBuffer.html#method.range_mut) when passing an empty range ([contributed by Icxolu in GH-4](https://github.com/andreacorbellini/rust-circular-buffer/pull/4)). ## circular-buffer 0.1.2 * Made [`extend_from_slice()`](https://docs.rs/circular-buffer/0.1.2/circular_buffer/struct.CircularBuffer.html#method.extend_from_slice) safer by ensuring that all cloned elements get dropped in case a panic occurs. * Optimized all [`PartialEq`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) implementations. * Fixed a [strict-provenance](https://github.com/rust-lang/rust/issues/95228) error in [`swap()`](https://docs.rs/circular-buffer/0.1.2/circular_buffer/struct.CircularBuffer.html#method.swap) ([contributed by René Kijewski in GH-2](https://github.com/andreacorbellini/rust-circular-buffer/pull/2)). ## circular-buffer 0.1.1 * Made circular-buffer compatible with the stable version of rustc. ## circular-buffer 0.1.0 * Initial release. circular-buffer-1.0.0/Cargo.lock0000644000000501740000000000100120670ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "circular-buffer" version = "1.0.0" dependencies = [ "criterion", "drop-tracker", "embedded-io", "embedded-io-async", "futures-lite", "rand", ] [[package]] name = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags", "textwrap", "unicode-width", ] [[package]] name = "criterion" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_cbor", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "csv" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "drop-tracker" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c59c89ce3a9cfb5bf9691fd33c4b295eb0fa3689ec8c20793f62dc97a6900a" [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "embedded-io" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" [[package]] name = "embedded-io-async" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" dependencies = [ "embedded-io", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", "futures-io", "parking", "pin-project-lite", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "half" version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "parking" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "plotters" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustversion" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_cbor" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", "serde", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" [[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] circular-buffer-1.0.0/Cargo.toml0000644000000037020000000000100121050ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.82" name = "circular-buffer" version = "1.0.0" authors = ["Andrea Corbellini "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Efficient, fixed-size, overwriting circular buffer" readme = "README.md" keywords = [ "circular-buffer", "buffer", "queue", "fifo", "small", ] categories = [ "data-structures", "no-std", ] license = "BSD-3-Clause" repository = "https://github.com/andreacorbellini/rust-circular-buffer" [package.metadata.cargo-all-features] denylist = ["use_std"] skip_feature_sets = [[ "std", "alloc", ]] [lib] name = "circular_buffer" path = "src/lib.rs" [[test]] name = "covariance" path = "tests/covariance.rs" [[test]] name = "io" path = "tests/io.rs" [[test]] name = "large" path = "tests/large.rs" [[test]] name = "randomized" path = "tests/randomized.rs" [[bench]] name = "benchmark" path = "benches/benchmark.rs" harness = false [dependencies.embedded-io] version = "0.6" optional = true default-features = false [dependencies.embedded-io-async] version = "0.6" optional = true default-features = false [dev-dependencies.criterion] version = "0.3" features = ["html_reports"] [dev-dependencies.drop-tracker] version = "0.1.3" [dev-dependencies.futures-lite] version = "2.6" [dev-dependencies.rand] version = "0.8" [features] alloc = [] default = ["std"] std = ["alloc"] unstable = [] use_std = ["std"] circular-buffer-1.0.0/Cargo.toml.orig000064400000000000000000000023131046102023000155630ustar 00000000000000[package] name = "circular-buffer" version = "1.0.0" authors = ["Andrea Corbellini "] edition = "2021" rust-version = "1.82" license = "BSD-3-Clause" description = "Efficient, fixed-size, overwriting circular buffer" repository = "https://github.com/andreacorbellini/rust-circular-buffer" keywords = ["circular-buffer", "buffer", "queue", "fifo", "small"] categories = ["data-structures", "no-std"] [features] default = ["std"] alloc = [] std = ["alloc"] # Deprecated in favor of `std` use_std = ["std"] unstable = [] [dependencies] embedded-io = { version = "0.6", default-features = false, optional = true } embedded-io-async = { version = "0.6", default-features = false, optional = true } [dev-dependencies] criterion = { version = "0.3", features = ["html_reports"] } drop-tracker = { version = "0.1.3" } rand = { version = "0.8" } futures-lite = { version = "2.6" } [[bench]] name = "benchmark" harness = false [package.metadata.cargo-all-features] denylist = [ # `use_std` is an alias for `std`, so there's no point in testing it "use_std", ] skip_feature_sets = [ # `std` requires `alloc`, so there's no point in testing both at the same time ["std", "alloc"], ] circular-buffer-1.0.0/LICENSE000064400000000000000000000027701046102023000137100ustar 00000000000000Copyright © 2023, 2024 Andrea Corbellini and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. circular-buffer-1.0.0/README.md000064400000000000000000000033321046102023000141550ustar 00000000000000# Circular Buffer for Rust [![Crate](https://img.shields.io/crates/v/circular-buffer)](https://crates.io/crates/circular-buffer) [![Documentation](https://img.shields.io/docsrs/circular-buffer)](https://docs.rs/circular-buffer/latest/circular_buffer/) [![License](https://img.shields.io/crates/l/circular-buffer)](https://choosealicense.com/licenses/bsd-3-clause/) This is a Rust crate that implements a [circular buffer], also known as cyclic buffer, circular queue or ring. This circular buffer has a fixed maximum capacity, does not automatically grow, and once its maximum capacity is reached, elements at the start of the buffer are overwritten. It's useful for implementing fast FIFO (_first in, first out_) and LIFO (_last in, first out_) queues with a fixed memory capacity. For more information and examples, check out the [documentation]! [circular buffer]: https://en.wikipedia.org/wiki/Circular_buffer [documentation]: https://docs.rs/circular-buffer/latest/circular_buffer/ # Changelog For a full list of changes between releases, visit [GitHub](https://github.com/andreacorbellini/rust-circular-buffer/releases). # Example ```rust use circular_buffer::CircularBuffer; // Initialize a new, empty circular buffer with a capacity of 5 elements let mut buf = CircularBuffer::<5, u32>::new(); // Add a few elements buf.push_back(1); buf.push_back(2); buf.push_back(3); assert_eq!(buf, [1, 2, 3]); // Add more elements to fill the buffer capacity completely buf.push_back(4); buf.push_back(5); assert_eq!(buf, [1, 2, 3, 4, 5]); // Adding more elements than the buffer can contain causes the front elements to be // automatically dropped buf.push_back(6); assert_eq!(buf, [2, 3, 4, 5, 6]); // `1` got dropped to make room for `6` ``` circular-buffer-1.0.0/benches/benchmark.rs000064400000000000000000000043221046102023000166050ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause use circular_buffer::CircularBuffer; use criterion::criterion_group; use criterion::criterion_main; use criterion::measurement::Measurement; use criterion::BenchmarkGroup; use criterion::Criterion; use criterion::Throughput; fn bench_push(c: &mut Criterion) { let mut group = c.benchmark_group("push"); fn do_bench( group: &mut BenchmarkGroup<'_, M>, buf: &mut CircularBuffer, ) { group.throughput(Throughput::Elements(N as u64)); group.bench_function("push_back", |b| { b.iter(|| { buf.push_back(1); }) }); group.bench_function("push_front", |b| { b.iter(|| { buf.push_front(1); }) }); } do_bench(&mut group, &mut CircularBuffer::<10, u32>::new()); do_bench(&mut group, &mut CircularBuffer::<100, u32>::new()); #[cfg(feature = "std")] do_bench(&mut group, &mut CircularBuffer::<1000, u32>::boxed()); group.finish(); } fn bench_pop(c: &mut Criterion) { let mut group = c.benchmark_group("pop"); fn do_bench( group: &mut BenchmarkGroup<'_, M>, buf: &mut CircularBuffer, ) { buf.fill(0); group.throughput(Throughput::Elements(N as u64)); group.bench_function("pop_back", |b| { b.iter(|| { let mut buf = buf.clone(); for _ in 0..buf.capacity() { buf.pop_back(); } }) }); group.bench_function("pop_front", |b| { b.iter(|| { let mut buf = buf.clone(); for _ in 0..buf.capacity() { buf.pop_front(); } }) }); } do_bench(&mut group, &mut CircularBuffer::<10, u32>::new()); do_bench(&mut group, &mut CircularBuffer::<100, u32>::new()); #[cfg(feature = "std")] do_bench(&mut group, &mut CircularBuffer::<1000, u32>::boxed()); group.finish(); } criterion_group!(benches, bench_push, bench_pop); criterion_main!(benches); circular-buffer-1.0.0/buildall000075500000000000000000000023501046102023000144130ustar 00000000000000#!/bin/bash set -ex toolchains=( +1.82 +stable +nightly ) rm -f Cargo.lock for toolchain in "${toolchains[@]}"; do export CARGO_TARGET_DIR=target/$toolchain cargo "$toolchain" build cargo "$toolchain" build --release cargo "$toolchain" test cargo "$toolchain" test --release if [[ $toolchain = +nightly ]]; then cargo "$toolchain" test --features unstable cargo "$toolchain" test --features unstable --release fi cargo "$toolchain" build --no-default-features cargo "$toolchain" build --no-default-features --release if [[ $toolchain = +nightly ]]; then cargo "$toolchain" build --no-default-features --features unstable cargo "$toolchain" build --no-default-features --features unstable --release fi done for toolchain in "${toolchains[@]}"; do export CARGO_TARGET_DIR=target/$toolchain cargo "$toolchain" clippy --all-targets if [[ $toolchain = +nightly ]]; then cargo "$toolchain" clippy --all-targets --features unstable fi done for toolchain in "${toolchains[@]}"; do export CARGO_TARGET_DIR=target/$toolchain if [[ $toolchain = +nightly ]]; then cargo "$toolchain" miri test fi done cargo semver-checks circular-buffer-1.0.0/circular-buffer.svg000064400000000000000000000041501046102023000164710ustar 00000000000000 front back 0 1 2 ··· n circular-buffer-1.0.0/heap-experiment.rs000064400000000000000000000056711046102023000163470ustar 00000000000000use std::alloc; use std::alloc::Layout; use std::mem; use std::ops::Deref; use std::ptr; struct Inner { start: usize, len: usize, buf: T, } pub struct Static { inner: Inner<[T; N]>, } pub struct Dynamic { inner: Box>, } #[repr(transparent)] pub struct Ref { inner: Inner<[T]>, } impl Deref for Static { type Target = Ref; fn deref(&self) -> &Self::Target { let inner: &Inner<[T]> = &self.inner; unsafe { mem::transmute::<&Inner<[T]>, &Ref>(inner) } } } impl Dynamic { #[inline] fn layout_for(capacity: usize) -> Layout { Layout::new::>() .extend(Layout::array::(capacity).expect("...")) .expect("...") .0 } #[inline] fn ptr_to_inner(ptr: *mut u8, capacity: usize) -> *mut Inner<[T]> { assert!(!ptr.is_null()); ptr::slice_from_raw_parts_mut(ptr.cast::(), capacity) as *mut Inner<[T]> } pub fn new(capacity: usize) -> Self { let layout = Self::layout_for(capacity); let inner = unsafe { let ptr = Self::ptr_to_inner(alloc::alloc(layout), capacity); ptr::addr_of_mut!((*ptr).start).write(0); ptr::addr_of_mut!((*ptr).len).write(0); Box::from_raw(ptr) }; Self { inner } } pub fn capacity(&self) -> usize { self.inner.buf.len() } pub fn grow(&mut self, new_capacity: usize) { assert!(new_capacity >= self.capacity()); let layout = Self::layout_for(self.capacity()); let new_layout = Self::layout_for(new_capacity); unsafe { let ptr = Box::into_raw(ptr::addr_of_mut!(self.inner).read()) as *mut u8; let new_ptr = Self::ptr_to_inner(alloc::realloc(ptr, layout, new_layout.size()), new_capacity); let inner = Box::from_raw(new_ptr); ptr::addr_of_mut!(self.inner).write(inner); } } } impl Deref for Dynamic { type Target = Ref; fn deref(&self) -> &Self::Target { let inner: &Inner<[T]> = &self.inner; unsafe { mem::transmute::<&Inner<[T]>, &Ref>(inner) } } } impl Ref { #[inline] pub fn len(&self) -> usize { self.inner.len } } #[cfg(test)] mod tests { use super::*; #[test] fn size() { // `Dynamic` should have the same size as a fat pointer assert_eq!(size_of::>(), size_of::<*mut [i32]>()); assert_eq!(size_of::>(), size_of::>()); assert_eq!(size_of::>(), size_of::<[usize; 2]>()); } #[test] fn capacity() { let mut b = Dynamic::::new(10); assert_eq!(b.capacity(), 10); b.grow(20); assert_eq!(b.capacity(), 20); } #[test] fn deref() { let b = Dynamic::::new(0); assert_eq!(b.len(), 0); } } circular-buffer-1.0.0/rustfmt.toml000064400000000000000000000000431046102023000152730ustar 00000000000000format_code_in_doc_comments = true circular-buffer-1.0.0/src/debug.rs000064400000000000000000000004451046102023000151230ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause use core::fmt; impl Debug for CircularBuffer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self).finish() } } circular-buffer-1.0.0/src/drain.rs000064400000000000000000000345641046102023000151430ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause use crate::add_mod; use crate::iter::translate_range_bounds; use crate::iter::Iter; use crate::CircularBuffer; use core::fmt; use core::iter::FusedIterator; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::ops::Range; use core::ops::RangeBounds; use core::ptr; use core::ptr::NonNull; /// A draining [iterator](core::iter::Iterator) that removes and returns elements from a /// `CircularBuffer`. /// /// This struct is created by [`CircularBuffer::drain()`]. See its documentation for more details. pub struct Drain<'a, const N: usize, T> { /// This is a pointer and not a reference (`&'a mut CircularBuffer`) because using a reference /// would make `Drain` an invariant over `CircularBuffer`, but instead we want `Drain` to be /// covariant over `CircularBuffer`. /// /// The reason why `Drain` needs to be covariant is that, semantically, /// `CircularBuffer::drain()` should be equivalent to popping all the drained elements from the /// buffer, storing them into a vector, and returning an iterable over the vector. /// Equivalently, `Drain` owns the drained elements, so it would be unnecessarily restrictive /// to make this type invariant over `CircularBuffer`. buf: NonNull>, /// A backup of the size of the buffer. Necessary because `buf.size` is set to 0 during the /// lifetime of the `Drain` and is restored only during drop. buf_size: usize, /// The range that was requested to drain. Necessary to properly rearrange the buffer memory /// during drop. range: Range, /// An iterator over the indexes of the elements to return from the draining iterator. /// Initially, `range` and `iter` are set to the same `Range`, but as the draining iterator is /// used (via `Iterator::next()`, or similar), `iter` is mutated, while `range` is preserved. iter: Range, /// Necessary to bind the lifetime of `CircularBuffer` to `Drain`. Note that this is an `&` /// reference, and not a mutable `&mut` reference: this is to make `Drain` covariant over /// `CircularBuffer`. phantom: PhantomData<&'a T>, } impl<'a, const N: usize, T> Drain<'a, N, T> { pub(crate) fn over_range(buf: &'a mut CircularBuffer, range: R) -> Self where R: RangeBounds, { let (start, end) = translate_range_bounds(buf, range); // Iterating over a `Drain` returns items from the buffer, but does actually remove the // item from the buffer right away. Because of that, forgetting a `Drain` (via // `mem::forget`) can potentially leave the `CircularBuffer` in an unsafe state: the same // item may have been returned from the `Drain` iterator, and be part of the // `CircularBuffer` at the same time, which would be unsafe for non-`Copy` types. // // To avoid getting into this unsafe state, the size of the buffer is set to 0 while the // `Drain` is alive, and it's restored when the `Drain` is dropped. Forgetting a `Drain` // will therefore forget all the items in the buffer (even the ones that were not drained). // This ensures maximum safety while keeping the implementation simple and performant // enough. let buf_size = buf.size; buf.size = 0; let buf = NonNull::from(buf); Self { buf, buf_size, range: start..end, iter: start..end, phantom: PhantomData, } } /// Reads an element from the `CircularBuffer`. /// /// # Safety /// /// The `index` must point to an initialized element of the buffer. After this method is used, /// the element at `index` must be considered as uninitialized memory and therefore the `index` /// must not be reused. unsafe fn read(&self, index: usize) -> T { debug_assert!( index < N && index < self.buf_size, "index out-of-bounds for buffer" ); debug_assert!( index >= self.range.start && index < self.range.end, "index out-of-bounds for drain range" ); debug_assert!( index < self.iter.start || index >= self.iter.end, "attempt to read an item that may be returned by the iterator" ); let buf = self.buf.as_ref(); let index = add_mod(buf.start, index, N); ptr::read(buf.items[index].assume_init_ref()) } fn as_slices(&self) -> (&[T], &[T]) { if N == 0 || self.buf_size == 0 || self.iter.is_empty() { return (&[][..], &[][..]); } let buf = unsafe { self.buf.as_ref() }; debug_assert!(buf.start < N, "start out-of-bounds"); debug_assert!(self.buf_size <= N, "size out-of-bounds"); let start = add_mod(buf.start, self.iter.start, N); let end = add_mod(buf.start, self.iter.end, N); let (right, left) = if start < end { (&buf.items[start..end], &[][..]) } else { let (left, right) = buf.items.split_at(end); let right = &right[start - end..]; (right, left) }; // SAFETY: The elements in these slices are guaranteed to be initialized #[cfg(feature = "unstable")] unsafe { ( MaybeUninit::slice_assume_init_ref(right), MaybeUninit::slice_assume_init_ref(left), ) } #[cfg(not(feature = "unstable"))] unsafe { ( &*(right as *const [MaybeUninit] as *const [T]), &*(left as *const [MaybeUninit] as *const [T]), ) } } fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { if N == 0 || self.buf_size == 0 || self.iter.is_empty() { return (&mut [][..], &mut [][..]); } let buf = unsafe { self.buf.as_mut() }; debug_assert!(buf.start < N, "start out-of-bounds"); debug_assert!(self.buf_size <= N, "size out-of-bounds"); let start = add_mod(buf.start, self.iter.start, N); let end = add_mod(buf.start, self.iter.end, N); let (right, left) = if start < end { (&mut buf.items[start..end], &mut [][..]) } else { let (left, right) = buf.items.split_at_mut(end); let right = &mut right[start - end..]; (right, left) }; // SAFETY: The elements in these slices are guaranteed to be initialized #[cfg(feature = "unstable")] unsafe { ( MaybeUninit::slice_assume_init_mut(right), MaybeUninit::slice_assume_init_mut(left), ) } #[cfg(not(feature = "unstable"))] unsafe { ( &mut *(right as *mut [MaybeUninit] as *mut [T]), &mut *(left as *mut [MaybeUninit] as *mut [T]), ) } } } impl Iterator for Drain<'_, N, T> { type Item = T; #[inline] fn next(&mut self) -> Option { // SAFETY: the element at the index is guaranteed to be initialized self.iter.next().map(|index| unsafe { self.read(index) }) } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl ExactSizeIterator for Drain<'_, N, T> { #[inline] fn len(&self) -> usize { self.iter.len() } } impl FusedIterator for Drain<'_, N, T> {} impl DoubleEndedIterator for Drain<'_, N, T> { fn next_back(&mut self) -> Option { // SAFETY: the element at the index is guaranteed to be initialized self.iter .next_back() .map(|index| unsafe { self.read(index) }) } } impl Drop for Drain<'_, N, T> { fn drop(&mut self) { // Drop the items that were not consumed struct Dropper<'a, T>(&'a mut [T]); impl Drop for Dropper<'_, T> { fn drop(&mut self) { // SAFETY: the slice is guaranteed to be valid for read and writes as the `Drain` // holds a mutable reference to the `CircularBuffer` that contains the data // referenced by the slices. unsafe { ptr::drop_in_place(self.0); } } } let (right, left) = self.as_mut_slices(); let right = Dropper(right); let left = Dropper(left); drop(right); drop(left); // The drain has left a "hole" of items in the `CircularBuffer` that either got moved out // during iteration, or got dropped earlier. There are 3 possible scenarios for the state // of the `CircularBuffer` at this point: // // 1. The "hole" is at the front of the buffer: // | hole | remaining items | // // 2. The "hole" is at the back of the buffer: // | remaining items | hole | // // 3. The "hole" is in the middle of the buffer: // | remaining items | hole | remaining items | // // Scenario #1 and #2 can be handled by adjusting the start offset and length of the // buffer. Scenario #3 requires moving the remaining items into the "hole" to fill the gap. // // Filling the hole for scenario #3 requires at most a 3-steps. The worst case looks like // this: // // | back items [part 2/2] | front items | hole | back items [part 1/2] | // ^ // ` start // // The first step to do is to move `back items [part 1/2]` into `hole`, so that the // `CircularBuffer` looks like this: // // | back items [part 2/2] | front items | back items [part 1/2] | hole | // ^ // ` start // // Then a portion of `back items [part 2/2]` can be copied into the new `hole`. Note that // `back items [part 2/2]` may not fit into `hole`, and so it may be necessary to split it // in two chunks: // // | hole | back items [part 3/3] | front items | back items [part 1/3] | back items [part 2/3] | // ^ // ` start // // Finally the last chunk `back items [part 3/3]` can be moved into that `hole`: // // | back items [part 3/3] | hole | front items | back items [part 1/3] | back items [part 2/3] | // ^ // ` start // // A similar strategy could be applied to move the front items into the hole instead of the // back items. Ideally the implementation should decide whether to move the front items or // the back items depending on which one results in fewer data to be moved; however for now // only the implementation always moves the back items. // TODO: optimize for the case where the hole is in the front or the back // TODO: optimize for the case where there are fewer items to move from the front // SAFETY: `buf` is a valid pointer because `Drain` holds a mutable reference to it. let buf = unsafe { self.buf.as_mut() }; let mut remaining = self.buf_size - self.range.end; let items = CircularSlicePtr::new(&mut buf.items).add(buf.start); let mut hole = items.add(self.range.start); let mut backfill = items.add(self.range.end); // This loop should run at most 3 times as explained above while remaining > 0 { let copy_len = hole .available_len() .min(backfill.available_len()) .min(remaining); // SAFETY: both pointers are properly aligned, and are valid for read and writes. unsafe { ptr::copy(backfill.as_ptr(), hole.as_mut_ptr(), copy_len) }; hole = hole.add(copy_len); backfill = backfill.add(copy_len); remaining -= copy_len; } // Now that the buffer memory contains valid items, the size can be restored buf.size = self.buf_size - self.range.len(); } } impl fmt::Debug for Drain<'_, N, T> where T: fmt::Debug, { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (right, left) = self.as_slices(); let it = Iter { right, left }; it.fmt(f) } } #[derive(Debug)] struct CircularSlicePtr<'a, T> { slice_start: *mut T, slice_len: usize, offset: usize, phantom: PhantomData<&'a T>, } impl<'a, T> CircularSlicePtr<'a, T> { fn new(slice: &'a mut [T]) -> Self { Self { slice_start: slice as *mut [T] as *mut T, slice_len: slice.len(), offset: 0, phantom: PhantomData, } } fn as_ptr(&self) -> *const T { debug_assert!(self.offset < self.slice_len); // SAFETY: `slice_start` is a valid pointer because it was obtained from a reference that // is still alive; `offset` is within the bounds of the slice, so the resulting pointer is // also valid. unsafe { self.slice_start.add(self.offset) } } fn as_mut_ptr(&self) -> *mut T { debug_assert!(self.offset < self.slice_len); // SAFETY: `slice_start` is a valid pointer because it was obtained from a reference that // is still alive; `offset` is within the bounds of the slice, so the resulting pointer is // also valid. unsafe { self.slice_start.add(self.offset) } } fn available_len(&self) -> usize { debug_assert!(self.offset < self.slice_len); self.slice_len - self.offset } fn add(mut self, increment: usize) -> Self { debug_assert!(self.offset < self.slice_len); debug_assert!(increment <= self.slice_len); self.offset = add_mod(self.offset, increment, self.slice_len); self } } // Need to manually implement `Copy` because `#[derive(Copy)]` requires `T` to implement `Copy`. // Also need to manually implement `Clone` because `Copy` requires `Clone`. impl Copy for CircularSlicePtr<'_, T> {} impl Clone for CircularSlicePtr<'_, T> { fn clone(&self) -> Self { *self } } circular-buffer-1.0.0/src/embedded_io.rs000064400000000000000000000053671046102023000162650ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause use crate::CircularBuffer; use core::convert::Infallible; #[cfg(feature = "embedded-io")] use embedded_io::ErrorType; #[cfg(all(feature = "embedded-io-async", not(feature = "embedded-io")))] use embedded_io_async::ErrorType; impl ErrorType for CircularBuffer { type Error = Infallible; } #[cfg(feature = "embedded-io")] impl embedded_io::Write for CircularBuffer { #[inline] fn write(&mut self, src: &[u8]) -> Result { self.extend_from_slice(src); Ok(src.len()) } #[inline] fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } #[cfg(feature = "embedded-io")] impl embedded_io::Read for CircularBuffer { fn read(&mut self, dst: &mut [u8]) -> Result { let (mut front, mut back) = self.as_slices(); let mut count = front.read(dst)?; count += back.read(&mut dst[count..])?; self.truncate_front(self.len() - count); Ok(count) } } #[cfg(feature = "embedded-io")] impl embedded_io::BufRead for CircularBuffer { fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { let (front, back) = self.as_slices(); if !front.is_empty() { Ok(front) } else { Ok(back) } } fn consume(&mut self, amt: usize) { let amt = core::cmp::min(amt, self.len()); self.drain(..amt); } } #[cfg(feature = "embedded-io-async")] impl embedded_io_async::Write for CircularBuffer { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } async fn write(&mut self, src: &[u8]) -> Result { self.extend_from_slice(src); Ok(src.len()) } } #[cfg(feature = "embedded-io-async")] impl embedded_io_async::Read for CircularBuffer { async fn read(&mut self, dst: &mut [u8]) -> Result { let (mut front, mut back) = self.as_slices(); let mut count = front.read(dst).await?; count += back.read(&mut dst[count..]).await?; self.truncate_front(self.len() - count); Ok(count) } } #[cfg(feature = "embedded-io-async")] impl embedded_io_async::BufRead for CircularBuffer { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { let (front, back) = self.as_slices(); if !front.is_empty() { Ok(front) } else { Ok(back) } } fn consume(&mut self, amt: usize) { let amt = core::cmp::min(amt, self.len()); self.drain(..amt); } } circular-buffer-1.0.0/src/io.rs000064400000000000000000000022641046102023000144450ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause use crate::CircularBuffer; use std::cmp; use std::io::BufRead; use std::io::Read; use std::io::Result; use std::io::Write; impl Write for CircularBuffer { #[inline] fn write(&mut self, src: &[u8]) -> Result { self.extend_from_slice(src); Ok(src.len()) } #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } } impl Read for CircularBuffer { fn read(&mut self, dst: &mut [u8]) -> Result { let (mut front, mut back) = self.as_slices(); let mut count = front.read(dst)?; count += back.read(&mut dst[count..])?; self.truncate_front(self.len() - count); Ok(count) } } impl BufRead for CircularBuffer { fn fill_buf(&mut self) -> Result<&[u8]> { let (front, back) = self.as_slices(); if !front.is_empty() { Ok(front) } else { Ok(back) } } fn consume(&mut self, amt: usize) { let amt = cmp::min(amt, self.len()); self.drain(..amt); } } circular-buffer-1.0.0/src/iter.rs000064400000000000000000000310621046102023000147770ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause use crate::CircularBuffer; use core::fmt; use core::iter::FusedIterator; use core::ops::Bound; use core::ops::RangeBounds; /// An owning [iterator](core::iter::Iterator) over the elements of a [`CircularBuffer`]. /// /// This yields the elements of a `CircularBuffer` from fron to back. /// /// This struct is created when iterating over a `CircularBuffer`. See the documentation for /// [`IntoIterator`] for more details. #[derive(Clone)] pub struct IntoIter { inner: CircularBuffer, } impl IntoIter { pub(crate) const fn new(inner: CircularBuffer) -> Self { Self { inner } } } impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { self.inner.pop_front() } #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.inner.len(); (len, Some(len)) } } impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.inner.len() } } impl FusedIterator for IntoIter {} impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { self.inner.pop_back() } } impl fmt::Debug for IntoIter where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } pub(crate) fn translate_range_bounds( buf: &CircularBuffer, range: R, ) -> (usize, usize) where R: RangeBounds, { let start = match range.start_bound() { Bound::Included(x) => *x, Bound::Excluded(x) => x .checked_add(1) .expect("range start index exceeds maximum usize"), Bound::Unbounded => 0, }; let end = match range.end_bound() { Bound::Included(x) => x .checked_add(1) .expect("range end index exceeds maximum usize"), Bound::Excluded(x) => *x, Bound::Unbounded => buf.len(), }; assert!( end <= buf.len(), "range end index {} out of range for buffer of length {}", end, buf.len() ); assert!( start <= end, "range starts at index {start} but ends at index {end}" ); (start, end) } #[inline(always)] #[cfg(feature = "unstable")] fn slice_take<'a, T, R: core::ops::OneSidedRange>( slice: &mut &'a [T], range: R, ) -> Option<&'a [T]> { slice.take(range) } #[cfg(not(feature = "unstable"))] fn slice_take<'a, T, R: RangeBounds>(slice: &mut &'a [T], range: R) -> Option<&'a [T]> { match (range.start_bound(), range.end_bound()) { (Bound::Unbounded, Bound::Excluded(index)) => { if *index > slice.len() { return None; } let (left, right) = slice.split_at(*index); *slice = right; Some(left) } (Bound::Included(index), Bound::Unbounded) => { if *index > slice.len() { return None; } let (left, right) = slice.split_at(*index); *slice = left; Some(right) } _ => unimplemented!(), } } #[inline(always)] #[cfg(feature = "unstable")] fn slice_take_mut<'a, T, R: core::ops::OneSidedRange>( slice: &mut &'a mut [T], range: R, ) -> Option<&'a mut [T]> { slice.take_mut(range) } #[cfg(not(feature = "unstable"))] fn slice_take_mut<'a, T, R: RangeBounds>( slice: &mut &'a mut [T], range: R, ) -> Option<&'a mut [T]> { match (range.start_bound(), range.end_bound()) { (Bound::Unbounded, Bound::Excluded(index)) => { if *index > slice.len() { return None; } let (left, right) = core::mem::take(slice).split_at_mut(*index); *slice = right; Some(left) } (Bound::Included(index), Bound::Unbounded) => { if *index > slice.len() { return None; } let (left, right) = core::mem::take(slice).split_at_mut(*index); *slice = left; Some(right) } _ => unimplemented!(), } } #[inline(always)] #[cfg(feature = "unstable")] fn slice_take_first<'a, T>(slice: &mut &'a [T]) -> Option<&'a T> { slice.take_first() } #[cfg(not(feature = "unstable"))] fn slice_take_first<'a, T>(slice: &mut &'a [T]) -> Option<&'a T> { let (item, rest) = slice.split_first()?; *slice = rest; Some(item) } #[inline(always)] #[cfg(feature = "unstable")] fn slice_take_first_mut<'a, T>(slice: &mut &'a mut [T]) -> Option<&'a mut T> { slice.take_first_mut() } #[cfg(not(feature = "unstable"))] fn slice_take_first_mut<'a, T>(slice: &mut &'a mut [T]) -> Option<&'a mut T> { let (item, rest) = core::mem::take(slice).split_first_mut()?; *slice = rest; Some(item) } #[inline(always)] #[cfg(feature = "unstable")] fn slice_take_last<'a, T>(slice: &mut &'a [T]) -> Option<&'a T> { slice.take_last() } #[cfg(not(feature = "unstable"))] fn slice_take_last<'a, T>(slice: &mut &'a [T]) -> Option<&'a T> { let (item, rest) = slice.split_last()?; *slice = rest; Some(item) } #[inline(always)] #[cfg(feature = "unstable")] fn slice_take_last_mut<'a, T>(slice: &mut &'a mut [T]) -> Option<&'a mut T> { slice.take_last_mut() } #[cfg(not(feature = "unstable"))] fn slice_take_last_mut<'a, T>(slice: &mut &'a mut [T]) -> Option<&'a mut T> { let (item, rest) = core::mem::take(slice).split_last_mut()?; *slice = rest; Some(item) } /// An [iterator](core::iter::Iterator) over the elements of a `CircularBuffer`. /// /// This struct is created by [`CircularBuffer::iter()`] and [`CircularBuffer::range()`]. See /// their documentation for more details. pub struct Iter<'a, T> { pub(crate) right: &'a [T], pub(crate) left: &'a [T], } impl<'a, T> Iter<'a, T> { pub(crate) const fn empty() -> Self { Self { right: &[], left: &[], } } pub(crate) fn new(buf: &'a CircularBuffer) -> Self { let (right, left) = buf.as_slices(); Self { right, left } } pub(crate) fn over_range(buf: &'a CircularBuffer, range: R) -> Self where R: RangeBounds, { let (start, end) = translate_range_bounds(buf, range); if start >= end { Self::empty() } else { let len = buf.len(); let mut it = Self::new(buf); it.advance_front_by(start); it.advance_back_by(len - end); it } } fn advance_front_by(&mut self, count: usize) { if self.right.len() > count { slice_take(&mut self.right, ..count); } else { let take_left = count - self.right.len(); debug_assert!( take_left <= self.left.len(), "attempted to advance past the back of the buffer" ); slice_take(&mut self.left, ..take_left); self.right = &[]; } } fn advance_back_by(&mut self, count: usize) { if self.left.len() > count { let take_left = self.left.len() - count; slice_take(&mut self.left, take_left..); } else { let take_right = self.right.len() - (count - self.left.len()); debug_assert!( take_right <= self.right.len(), "attempted to advance past the front of the buffer" ); slice_take(&mut self.right, take_right..); self.left = &[]; } } } impl Default for Iter<'_, T> { #[inline] fn default() -> Self { Self::empty() } } impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { if let Some(item) = slice_take_first(&mut self.right) { Some(item) } else if let Some(item) = slice_take_first(&mut self.left) { Some(item) } else { None } } #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } } impl ExactSizeIterator for Iter<'_, T> { #[inline] fn len(&self) -> usize { self.right.len() + self.left.len() } } impl FusedIterator for Iter<'_, T> {} impl DoubleEndedIterator for Iter<'_, T> { fn next_back(&mut self) -> Option { if let Some(item) = slice_take_last(&mut self.left) { Some(item) } else if let Some(item) = slice_take_last(&mut self.right) { Some(item) } else { None } } } impl Clone for Iter<'_, T> { fn clone(&self) -> Self { Self { right: self.right, left: self.left, } } } impl fmt::Debug for Iter<'_, T> where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } /// A mutable [iterator](core::iter::Iterator) over the elements of a `CircularBuffer`. /// /// This struct is created by [`CircularBuffer::iter_mut()`] and [`CircularBuffer::range_mut()`]. /// See their documentation for more details. pub struct IterMut<'a, T> { right: &'a mut [T], left: &'a mut [T], } impl<'a, T> IterMut<'a, T> { pub(crate) fn empty() -> Self { Self { right: &mut [], left: &mut [], } } pub(crate) fn new(buf: &'a mut CircularBuffer) -> Self { let (right, left) = buf.as_mut_slices(); Self { right, left } } pub(crate) fn over_range(buf: &'a mut CircularBuffer, range: R) -> Self where R: RangeBounds, { let (start, end) = translate_range_bounds(buf, range); if start >= end { Self::empty() } else { let len = buf.len(); let mut it = Self::new(buf); it.advance_front_by(start); it.advance_back_by(len - end); it } } fn advance_front_by(&mut self, count: usize) { if self.right.len() > count { slice_take_mut(&mut self.right, ..count); } else { let take_left = count - self.right.len(); debug_assert!( take_left <= self.left.len(), "attempted to advance past the back of the buffer" ); slice_take_mut(&mut self.left, ..take_left); self.right = &mut []; } } fn advance_back_by(&mut self, count: usize) { if self.left.len() > count { let take_left = self.left.len() - count; slice_take_mut(&mut self.left, take_left..); } else { let take_right = self.right.len() - (count - self.left.len()); debug_assert!( take_right <= self.right.len(), "attempted to advance past the front of the buffer" ); slice_take_mut(&mut self.right, take_right..); self.left = &mut []; } } } impl Default for IterMut<'_, T> { #[inline] fn default() -> Self { Self::empty() } } impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option { if let Some(item) = slice_take_first_mut(&mut self.right) { Some(item) } else if let Some(item) = slice_take_first_mut(&mut self.left) { Some(item) } else { None } } #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } } impl ExactSizeIterator for IterMut<'_, T> { #[inline] fn len(&self) -> usize { self.right.len() + self.left.len() } } impl FusedIterator for IterMut<'_, T> {} impl DoubleEndedIterator for IterMut<'_, T> { fn next_back(&mut self) -> Option { if let Some(item) = slice_take_last_mut(&mut self.left) { Some(item) } else if let Some(item) = slice_take_last_mut(&mut self.right) { Some(item) } else { None } } } impl fmt::Debug for IterMut<'_, T> where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let it = Iter { right: self.right, left: self.left, }; it.fmt(f) } } circular-buffer-1.0.0/src/lib.rs000064400000000000000000002154561046102023000146150ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause //! This crate implements a [circular buffer], also known as cyclic buffer, circular queue or ring. //! //! The main struct is [`CircularBuffer`]. It can live on the stack and does not require any heap //! memory allocation. A `CircularBuffer` is sequence of elements with a maximum capacity: elements //! can be added to the buffer, and once the maximum capacity is reached, the elements at the start //! of the buffer are discarded and overwritten. //! //! [circular buffer]: https://en.wikipedia.org/wiki/Circular_buffer //! //! # Examples //! //! ``` //! use circular_buffer::CircularBuffer; //! //! // Initialize a new, empty circular buffer with a capacity of 5 elements //! let mut buf = CircularBuffer::<5, u32>::new(); //! //! // Add a few elements //! buf.push_back(1); //! buf.push_back(2); //! buf.push_back(3); //! assert_eq!(buf, [1, 2, 3]); //! //! // Add more elements to fill the buffer capacity completely //! buf.push_back(4); //! buf.push_back(5); //! assert_eq!(buf, [1, 2, 3, 4, 5]); //! //! // Adding more elements than the buffer can contain causes the front elements to be //! // automatically dropped //! buf.push_back(6); //! assert_eq!(buf, [2, 3, 4, 5, 6]); // `1` got dropped to make room for `6` //! ``` //! //! # Interface //! //! [`CircularBuffer`] provides methods akin the ones for the standard //! [`VecDeque`](std::collections::VecDeque) and [`LinkedList`](std::collections::LinkedList). The //! list below includes the most common methods, but see the [`CircularBuffer` struct //! documentation](CircularBuffer) to see more. //! //! ## Adding/removing elements //! //! * [`push_back()`](CircularBuffer::push_back), [`push_front()`](CircularBuffer::push_front) //! * [`pop_back()`](CircularBuffer::pop_back), [`pop_front()`](CircularBuffer::pop_front) //! * [`swap_remove_back()`](CircularBuffer::swap_remove_back), //! [`swap_remove_front()`](CircularBuffer::swap_remove_front) //! //! ## Getting/mutating elements //! //! * [`front()`](CircularBuffer::front), [`front_mut()`](CircularBuffer::front_mut) //! * [`back()`](CircularBuffer::back), [`back_mut()`](CircularBuffer::back_mut) //! * [`get()`](CircularBuffer::get), [`get_mut()`](CircularBuffer::get_mut) //! //! ## Adding multiple elements at once //! //! * [`extend()`](CircularBuffer::extend), //! [`extend_from_slice()`](CircularBuffer::extend_from_slice) //! * [`fill()`](CircularBuffer::fill), [`fill_with()`](CircularBuffer::fill_with) //! * [`fill_spare()`](CircularBuffer::fill), [`fill_spare_with()`](CircularBuffer::fill_with) //! //! ## Iterators //! //! * [`into_iter()`](CircularBuffer::into_iter) //! * [`iter()`](CircularBuffer::iter), [`iter_mut()`](CircularBuffer::iter_mut) //! * [`range()`](CircularBuffer::range), [`range_mut()`](CircularBuffer::range_mut) //! * [`drain()`](CircularBuffer::drain) //! //! ## Writing/reading bytes //! //! For the special case of a `CircularBuffer` containing `u8` elements, bytes can be written and //! read using the standard [`Write`](std::io::Write) and [`Read`](std::io::Read) traits. Writing //! past the buffer capacity will overwrite the bytes at the start of the buffer, and reading //! elements will consume elements from the buffer. //! //! ``` //! # #[allow(unused_must_use)] //! # #[cfg(feature = "std")] //! # { //! use circular_buffer::CircularBuffer; //! use std::io::Read; //! use std::io::Write; //! //! let mut buf = CircularBuffer::<5, u8>::new(); //! assert_eq!(buf, b""); //! //! write!(buf, "hello"); //! assert_eq!(buf, b"hello"); //! //! write!(buf, "this string will overflow the buffer and wrap around"); //! assert_eq!(buf, b"round"); //! //! let mut s = String::new(); //! buf.read_to_string(&mut s) //! .expect("failed to read from buffer"); //! assert_eq!(s, "round"); //! assert_eq!(buf, b""); //! # } //! ``` //! //! # Time complexity //! //! Most of the methods implemented by [`CircularBuffer`] run in constant time. Some of the methods //! may run in linear time if the type of the elements implements [`Drop`], as each element needs //! to be deallocated one-by-one. //! //! | Method | Complexity | //! |--------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| //! | [`push_back()`](CircularBuffer::push_back), [`push_front()`](CircularBuffer::push_front) | *O*(1) | //! | [`pop_back()`](CircularBuffer::pop_back), [`pop_front()`](CircularBuffer::pop_front) | *O*(1) | //! | [`remove(i)`](CircularBuffer::remove) | *O*(*n* − *i*) | //! | [`truncate_back(i)`](CircularBuffer::truncate_back), [`truncate_front(i)`](CircularBuffer::truncate_front) | *O*(*n* − *i*) for types that implement [`Drop`], *O*(1) otherwise | //! | [`clear()`](CircularBuffer::clear) | *O*(*n*) for types that implement [`Drop`], *O*(1) otherwise | //! | [`drain(i..j)`](CircularBuffer::drain) | *O*(*n* − *j*) | //! | [`fill()`](CircularBuffer::fill), [`fill_with()`](CircularBuffer::fill_with) | *O*(*c* + *n*) for types that implement [`Drop`], *O*(*n*) otherwise | //! | [`fill_spare()`](CircularBuffer::fill_spare), [`fill_spare_with()`](CircularBuffer::fill_spare_with) | *O*(*c* − *n*) | //! | [`front()`](CircularBuffer::front), [`back()`](CircularBuffer::back), [`get()`](CircularBuffer::get) | *O*(1) | //! | [`swap()`](CircularBuffer::swap), [`swap_remove_front()`](CircularBuffer::swap_remove_front), [`swap_remove_back()`](CircularBuffer::swap_remove_back) | *O*(1) | //! | [`as_slices()`](CircularBuffer::as_slices), [`as_mut_slices()`](CircularBuffer::as_mut_slices) | *O*(1) | //! | [`len()`](CircularBuffer::len), [`capacity()`](CircularBuffer::capacity) | *O*(1) | //! //! Notation: *n* is the [length](CircularBuffer::len) of the buffer, *c* is the //! [capacity](CircularBuffer::capacity) of the buffer, *i* and *j* are variables. //! //! # Stack vs heap //! //! The [`CircularBuffer`] struct is compact and has a fixed size, so it may live on the stack. //! This can provide optimal performance for small buffers as memory allocation can be avoided. //! //! For large buffers, or for buffers that need to be passed around often, it can be useful to //! allocate the buffer on the heap. Use a [`Box`](std::boxed) for that: //! //! ``` //! # #[cfg(feature = "std")] //! # { //! use circular_buffer::CircularBuffer; //! //! let mut buf = CircularBuffer::<4096, u32>::boxed(); //! assert_eq!(buf.len(), 0); //! //! for i in 0..1024 { //! buf.push_back(i); //! } //! assert_eq!(buf.len(), 1024); //! //! buf.truncate_back(128); //! assert_eq!(buf.len(), 128); //! # } //! ``` //! //! # `no_std` //! //! This crate can be used in a [`no_std` environment], although the I/O features and //! heap-allocation features won't be available by default in `no_std` mode. By default, this crate //! uses `std`; to use this crate in `no_std` mode, disable the default features for this crate in //! your `Cargo.toml`: //! //! ```text //! [dependencies] //! circular-buffer = { version = "0.1", default-features = false } //! ``` //! //! When using `no_std` mode, this crate supports heap-allocation features through the [`alloc` //! crate](alloc). To enable the use of the `alloc` crate, enable the `alloc` feature: //! //! ```text //! [dependencies] //! circular-buffer = { version = "0.1", default-features = false, features = ["alloc"] } //! ``` //! //! [`no_std` environment]: https://docs.rust-embedded.org/book/intro/no-std.html //! //! # Cargo feature flags //! //! * `std`: enables support for the [`std` library](https://doc.rust-lang.org/std/) (enabled by //! default). //! * `alloc`: enables support for the [`alloc` crate](https://doc.rust-lang.org/alloc/) (enabled //! by default). //! * `embedded-io`: enables implementation for the //! [`embedded_io`](https://docs.rs/embedded-io/) traits. //! * `embedded-io-async`: enables implementation for the //! [`embedded_io_async`](https://docs.rs/embedded-io-async) traits. #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "unstable", feature(maybe_uninit_slice))] #![cfg_attr(feature = "unstable", feature(maybe_uninit_uninit_array))] #![cfg_attr(feature = "unstable", feature(maybe_uninit_write_slice))] #![cfg_attr(feature = "unstable", feature(one_sided_range))] #![cfg_attr(feature = "unstable", feature(slice_take))] #![warn(missing_debug_implementations)] #![warn(missing_docs)] #![warn(unreachable_pub)] #![warn(unused_qualifications)] #![doc(test(attr(deny(warnings))))] mod drain; mod iter; #[cfg(feature = "std")] mod io; #[cfg(any(feature = "embedded-io", feature = "embedded-io-async"))] mod embedded_io; #[cfg(test)] mod tests; use core::cmp::Ordering; use core::fmt; use core::hash::Hash; use core::hash::Hasher; use core::mem; use core::mem::MaybeUninit; use core::ops::Index; use core::ops::IndexMut; use core::ops::Range; use core::ops::RangeBounds; use core::ptr; pub use crate::drain::Drain; pub use crate::iter::IntoIter; pub use crate::iter::Iter; pub use crate::iter::IterMut; #[cfg(feature = "alloc")] extern crate alloc; #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::boxed::Box; #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::vec::Vec; /// Returns `(x + y) % m` without risk of overflows if `x + y` cannot fit in `usize`. /// /// `x` and `y` are expected to be less than, or equal to `m`. #[inline] const fn add_mod(x: usize, y: usize, m: usize) -> usize { debug_assert!(m > 0); debug_assert!(x <= m); debug_assert!(y <= m); let (z, overflow) = x.overflowing_add(y); (z + (overflow as usize) * (usize::MAX % m + 1)) % m } /// Returns `(x - y) % m` without risk of underflows if `x - y` is negative. /// /// `x` and `y` are expected to be less than, or equal to `m`. #[inline] const fn sub_mod(x: usize, y: usize, m: usize) -> usize { debug_assert!(m > 0); debug_assert!(x <= m); debug_assert!(y <= m); add_mod(x, m - y, m) } #[inline] const unsafe fn slice_assume_init_ref(slice: &[MaybeUninit]) -> &[T] { #[cfg(feature = "unstable")] { MaybeUninit::slice_assume_init_ref(slice) } #[cfg(not(feature = "unstable"))] { &*(slice as *const [MaybeUninit] as *const [T]) } } #[inline] unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] { #[cfg(feature = "unstable")] { MaybeUninit::slice_assume_init_mut(slice) } #[cfg(not(feature = "unstable"))] { &mut *(slice as *mut [MaybeUninit] as *mut [T]) } } /// A fixed-size circular buffer. /// /// A `CircularBuffer` may live on the stack. Wrap the `CircularBuffer` in a [`Box`](std::boxed) /// using [`CircularBuffer::boxed()`] if you need the struct to be heap-allocated. /// /// See the [module-level documentation](self) for more details and examples. pub struct CircularBuffer { size: usize, start: usize, items: [MaybeUninit; N], } impl CircularBuffer { /// Returns an empty `CircularBuffer`. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// let buf = CircularBuffer::<16, u32>::new(); /// assert_eq!(buf, []); /// ``` #[inline] #[must_use] pub const fn new() -> Self { #[cfg(feature = "unstable")] { Self { size: 0, start: 0, items: MaybeUninit::uninit_array(), } } #[cfg(not(feature = "unstable"))] { Self { size: 0, start: 0, items: unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() }, } } } /// Returns an empty heap-allocated `CircularBuffer`. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// let buf = CircularBuffer::<1024, f64>::boxed(); /// assert_eq!(buf.len(), 0); /// ``` #[must_use] #[cfg(feature = "alloc")] pub fn boxed() -> Box { let mut uninit: Box> = Box::new_uninit(); let ptr = uninit.as_mut_ptr(); unsafe { // SAFETY: the pointer contains enough memory to contain `Self` and `addr_of_mut` // ensures that the address written to is properly aligned. core::ptr::addr_of_mut!((*ptr).size).write(0); core::ptr::addr_of_mut!((*ptr).start).write(0); // SAFETY: `size` and `start` have been properly initialized to 0; `items` does not // need to be initialized if `size` is 0 uninit.assume_init() } } /// Returns the number of elements in the buffer. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<16, u32>::new(); /// assert_eq!(buf.len(), 0); /// /// buf.push_back(1); /// buf.push_back(2); /// buf.push_back(3); /// assert_eq!(buf.len(), 3); /// ``` #[inline] pub const fn len(&self) -> usize { self.size } /// Returns the capacity of the buffer. /// /// This is the maximum number of elements that the buffer can hold. /// /// This method always returns the generic const parameter `N`. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// let buf = CircularBuffer::<16, u32>::new(); /// assert_eq!(buf.capacity(), 16); /// ``` #[inline] pub const fn capacity(&self) -> usize { N } /// Returns `true` if the buffer contains 0 elements. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<16, u32>::new(); /// assert!(buf.is_empty()); /// /// buf.push_back(1); /// assert!(!buf.is_empty()); /// ``` #[inline] pub const fn is_empty(&self) -> bool { self.size == 0 } /// Returns `true` if the number of elements in the buffer matches the buffer capacity. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<5, u32>::new(); /// assert!(!buf.is_full()); /// /// buf.push_back(1); /// assert!(!buf.is_full()); /// /// buf.push_back(2); /// buf.push_back(3); /// buf.push_back(4); /// buf.push_back(5); /// assert!(buf.is_full()); /// ``` #[inline] pub const fn is_full(&self) -> bool { self.size == N } /// Returns an iterator over the elements of the buffer. /// /// The iterator advances from front to back. Use [`.rev()`](Iter::rev) to advance from /// back to front. /// /// # Examples /// /// Iterate from front to back: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let buf = CircularBuffer::<5, char>::from_iter("abc".chars()); /// let mut it = buf.iter(); /// /// assert_eq!(it.next(), Some(&'a')); /// assert_eq!(it.next(), Some(&'b')); /// assert_eq!(it.next(), Some(&'c')); /// assert_eq!(it.next(), None); /// ``` /// /// Iterate from back to front: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let buf = CircularBuffer::<5, char>::from_iter("abc".chars()); /// let mut it = buf.iter().rev(); /// /// assert_eq!(it.next(), Some(&'c')); /// assert_eq!(it.next(), Some(&'b')); /// assert_eq!(it.next(), Some(&'a')); /// assert_eq!(it.next(), None); /// ``` #[inline] #[must_use] pub fn iter(&self) -> Iter<'_, T> { Iter::new(self) } /// Returns an iterator over the elements of the buffer that allows modifying each value. /// /// The iterator advances from front to back. Use [`.rev()`](Iter::rev) to advance from back to /// front. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<5, u32>::from([1, 2, 3]); /// for elem in buf.iter_mut() { /// *elem += 5; /// } /// assert_eq!(buf, [6, 7, 8]); /// ``` #[inline] #[must_use] pub fn iter_mut(&mut self) -> IterMut<'_, T> { IterMut::new(self) } /// Returns an iterator over the specified range of elements of the buffer. /// /// The iterator advances from front to back. Use [`.rev()`](Iter::rev) to advance from back to /// front. /// /// # Panics /// /// If the start of the range is greater than the end, or if the end is greater than the length /// of the buffer. /// /// # Examples /// /// Iterate from front to back: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let buf = CircularBuffer::<16, char>::from_iter("abcdefghi".chars()); /// let mut it = buf.range(3..6); /// /// assert_eq!(it.next(), Some(&'d')); /// assert_eq!(it.next(), Some(&'e')); /// assert_eq!(it.next(), Some(&'f')); /// assert_eq!(it.next(), None); /// ``` /// /// Iterate from back to front: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let buf = CircularBuffer::<16, char>::from_iter("abcdefghi".chars()); /// let mut it = buf.range(3..6).rev(); /// /// assert_eq!(it.next(), Some(&'f')); /// assert_eq!(it.next(), Some(&'e')); /// assert_eq!(it.next(), Some(&'d')); /// assert_eq!(it.next(), None); /// ``` #[inline] #[must_use] pub fn range(&self, range: R) -> Iter<'_, T> where R: RangeBounds, { Iter::over_range(self, range) } /// Returns an iterator over the specified range of elements of the buffer that allows /// modifying each value. /// /// The iterator advances from front to back. Use [`.rev()`](Iter::rev) to advance from back to /// front. /// /// # Panics /// /// If the start of the range is greater than the end, or if the end is greater than the length /// of the buffer. /// /// # Examples /// /// Iterate from front to back: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<16, i32>::from_iter([1, 2, 3, 4, 5, 6]); /// for elem in buf.range_mut(..3) { /// *elem *= -1; /// } /// assert_eq!(buf, [-1, -2, -3, 4, 5, 6]); /// ``` #[inline] #[must_use] pub fn range_mut(&mut self, range: R) -> IterMut<'_, T> where R: RangeBounds, { IterMut::over_range(self, range) } /// Removes the specified range from the buffer in bulk, returning the removed elements as an /// iterator. If the iterator is dropped before being fully consumed, it drops the remaining /// removed elements. /// /// # Panics /// /// If the start of the range is greater than the end, or if the end is greater than the length /// of the buffer. /// /// # Leaking /// /// If the returned iterator goes out of scope without being dropped (for example, due to /// calling [`mem::forget()`] on it), the buffer may have lost and leaked arbitrary elements, /// including elements outside of the range. /// /// The current implementation leaks all the elements of the buffer if the iterator is leaked, /// but this behavior may change in the future. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<6, char>::from_iter("abcdef".chars()); /// let drained = buf.drain(3..).collect::>(); /// /// assert_eq!(drained, ['d', 'e', 'f']); /// assert_eq!(buf, ['a', 'b', 'c']); /// ``` /// /// Not consuming the draining iterator still removes the range of elements: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<6, char>::from_iter("abcdef".chars()); /// buf.drain(3..); /// /// assert_eq!(buf, ['a', 'b', 'c']); /// ``` #[inline] pub fn drain(&mut self, range: R) -> Drain<'_, N, T> where R: RangeBounds, { Drain::over_range(self, range) } /// Rearranges the internal memory of the buffer so that all elements are in a contiguous /// slice, which is then returned. /// /// This method does not allocate and does not change the order of the inserted elements. /// Because it returns a mutable slice, any [slice methods](slice) may be called on the /// elements of the buffer, such as sorting methods. /// /// Once the internal storage is contiguous, the [`as_slices()`](CircularBuffer::as_slices) and /// [`as_mut_slices()`](CircularBuffer::as_mut_slices) methods will return the entire contents /// of the deque in a single slice. Adding new elements to the buffer may make the buffer /// disjoint (not contiguous). /// /// # Complexity /// /// If the buffer is disjoint (not contiguous), this method takes *O*(*N*) time, where *N* is /// the capacity of the buffer. /// /// If the buffer is already contiguous, this method takes *O*(1) time. /// /// This means that this method may be called multiple times on the same buffer without a /// performance penalty (provided that no new elements are added to the buffer in between /// calls). /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// // Create a new buffer, adding more elements than its capacity /// let mut buf = CircularBuffer::<4, u32>::from_iter([1, 4, 3, 0, 2, 5]); /// assert_eq!(buf, [3, 0, 2, 5]); /// /// // The buffer is disjoint: as_slices() returns two non-empty slices /// assert_eq!(buf.as_slices(), (&[3, 0][..], &[2, 5][..])); /// /// // Make the buffer contiguous /// assert_eq!(buf.make_contiguous(), &mut [3, 0, 2, 5]); /// // as_slices() now returns a single non-empty slice /// assert_eq!(buf.as_slices(), (&[3, 0, 2, 5][..], &[][..])); /// // The order of the elements in the buffer did not get modified /// assert_eq!(buf, [3, 0, 2, 5]); /// /// // Make the buffer contiguous and sort its elements /// buf.make_contiguous().sort(); /// assert_eq!(buf, [0, 2, 3, 5]); /// ``` pub fn make_contiguous(&mut self) -> &mut [T] { if N == 0 || self.size == 0 { return &mut []; } debug_assert!(self.start < N, "start out-of-bounds"); debug_assert!(self.size <= N, "size out-of-bounds"); let start = self.start; let end = add_mod(self.start, self.size, N); let slice = if start < end { // Already contiguous; nothing to do &mut self.items[start..end] } else { // Not contiguous; need to rotate self.start = 0; self.items.rotate_left(start); &mut self.items[..self.size] }; // SAFETY: The elements in the slice are guaranteed to be initialized unsafe { slice_assume_init_mut(slice) } } /// Returns a pair of slices which contain the elements of this buffer. /// /// The second slice may be empty if the internal buffer is contiguous. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// buf.push_back('d'); /// /// // Buffer is contiguous; second slice is empty /// assert_eq!(buf.as_slices(), (&['a', 'b', 'c', 'd'][..], &[][..])); /// /// buf.push_back('e'); /// buf.push_back('f'); /// /// // Buffer is disjoint; both slices are non-empty /// assert_eq!(buf.as_slices(), (&['c', 'd'][..], &['e', 'f'][..])); /// ``` #[inline] pub fn as_slices(&self) -> (&[T], &[T]) { if N == 0 || self.size == 0 { return (&[], &[]); } debug_assert!(self.start < N, "start out-of-bounds"); debug_assert!(self.size <= N, "size out-of-bounds"); let start = self.start; let end = add_mod(self.start, self.size, N); let (front, back) = if start < end { (&self.items[start..end], &[][..]) } else { let (back, front) = self.items.split_at(start); (front, &back[..end]) }; // SAFETY: The elements in these slices are guaranteed to be initialized unsafe { (slice_assume_init_ref(front), slice_assume_init_ref(back)) } } /// Returns a pair of mutable slices which contain the elements of this buffer. /// /// These slices can be used to modify or replace the elements in the buffer. /// /// The second slice may be empty if the internal buffer is contiguous. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// buf.push_back('d'); /// buf.push_back('e'); /// buf.push_back('f'); /// /// assert_eq!(buf, ['c', 'd', 'e', 'f']); /// /// let (left, right) = buf.as_mut_slices(); /// assert_eq!(left, &mut ['c', 'd'][..]); /// assert_eq!(right, &mut ['e', 'f'][..]); /// /// left[0] = 'z'; /// /// assert_eq!(buf, ['z', 'd', 'e', 'f']); /// ``` #[inline] pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { if N == 0 || self.size == 0 { return (&mut [][..], &mut [][..]); } debug_assert!(self.start < N, "start out-of-bounds"); debug_assert!(self.size <= N, "size out-of-bounds"); let start = self.start; let end = add_mod(self.start, self.size, N); let (front, back) = if start < end { (&mut self.items[start..end], &mut [][..]) } else { let (back, front) = self.items.split_at_mut(start); (front, &mut back[..end]) }; // SAFETY: The elements in these slices are guaranteed to be initialized unsafe { (slice_assume_init_mut(front), slice_assume_init_mut(back)) } } #[inline] fn front_maybe_uninit_mut(&mut self) -> &mut MaybeUninit { debug_assert!(self.size > 0, "empty buffer"); debug_assert!(self.start < N, "start out-of-bounds"); &mut self.items[self.start] } #[inline] const fn front_maybe_uninit(&self) -> &MaybeUninit { debug_assert!(self.size > 0, "empty buffer"); debug_assert!(self.size <= N, "size out-of-bounds"); debug_assert!(self.start < N, "start out-of-bounds"); &self.items[self.start] } #[inline] const fn back_maybe_uninit(&self) -> &MaybeUninit { debug_assert!(self.size > 0, "empty buffer"); debug_assert!(self.size <= N, "size out-of-bounds"); debug_assert!(self.start < N, "start out-of-bounds"); let back = add_mod(self.start, self.size - 1, N); &self.items[back] } #[inline] fn back_maybe_uninit_mut(&mut self) -> &mut MaybeUninit { debug_assert!(self.size > 0, "empty buffer"); debug_assert!(self.size <= N, "size out-of-bounds"); debug_assert!(self.start < N, "start out-of-bounds"); let back = add_mod(self.start, self.size - 1, N); &mut self.items[back] } #[inline] const fn get_maybe_uninit(&self, index: usize) -> &MaybeUninit { debug_assert!(self.size > 0, "empty buffer"); debug_assert!(index < N, "index out-of-bounds"); debug_assert!(self.start < N, "start out-of-bounds"); let index = add_mod(self.start, index, N); &self.items[index] } #[inline] fn get_maybe_uninit_mut(&mut self, index: usize) -> &mut MaybeUninit { debug_assert!(self.size > 0, "empty buffer"); debug_assert!(index < N, "index out-of-bounds"); debug_assert!(self.start < N, "start out-of-bounds"); let index = add_mod(self.start, index, N); &mut self.items[index] } #[inline] fn slices_uninit_mut(&mut self) -> (&mut [MaybeUninit], &mut [MaybeUninit]) { if N == 0 { return (&mut [][..], &mut [][..]); } debug_assert!(self.start < N, "start out-of-bounds"); debug_assert!(self.size <= N, "size out-of-bounds"); let start = self.start; let end = add_mod(start, self.size, N); if end < start { (&mut self.items[end..start], &mut [][..]) } else { let (left, right) = self.items.split_at_mut(end); let left = &mut left[..start]; (right, left) } } #[inline] fn inc_start(&mut self) { debug_assert!(self.start < N, "start out-of-bounds"); self.start = add_mod(self.start, 1, N); } #[inline] fn dec_start(&mut self) { debug_assert!(self.start < N, "start out-of-bounds"); self.start = sub_mod(self.start, 1, N); } #[inline] fn inc_size(&mut self) { debug_assert!(self.size <= N, "size out-of-bounds"); debug_assert!(self.size < N, "size at capacity limit"); self.size += 1; } #[inline] fn dec_size(&mut self) { debug_assert!(self.size > 0, "size is 0"); self.size -= 1; } #[inline] unsafe fn drop_range(&mut self, range: Range) { if range.is_empty() { return; } debug_assert!(self.start < N, "start out-of-bounds"); debug_assert!(self.size <= N, "size out-of-bounds"); debug_assert!(range.start < self.size, "start of range out-of-bounds"); debug_assert!(range.end <= self.size, "end of range out-of-bounds"); debug_assert!(range.start < range.end, "start of range is past its end"); debug_assert!( range.start == 0 || range.end == self.size, "range does not include boundary of the buffer" ); // Drops all the items in the slice when dropped. This is needed to ensure that all // elements are dropped in case a panic occurs during the drop of a single element. struct Dropper<'a, T>(&'a mut [MaybeUninit]); impl Drop for Dropper<'_, T> { #[inline] fn drop(&mut self) { // SAFETY: the caller of `drop_range` is responsible to check that this slice was // initialized. unsafe { ptr::drop_in_place(slice_assume_init_mut(self.0)); } } } let drop_from = add_mod(self.start, range.start, N); let drop_to = add_mod(self.start, range.end, N); let (right, left) = if drop_from < drop_to { (&mut self.items[drop_from..drop_to], &mut [][..]) } else { let (left, right) = self.items.split_at_mut(drop_from); let left = &mut left[..drop_to]; (right, left) }; let _left = Dropper(left); let _right = Dropper(right); } /// Returns a reference to the back element, or `None` if the buffer is empty. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// assert_eq!(buf.back(), None); /// /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// assert_eq!(buf.back(), Some(&'c')); /// ``` #[inline] pub fn back(&self) -> Option<&T> { if N == 0 || self.size == 0 { // Nothing to do return None; } // SAFETY: `size` is non-zero; back element is guaranteed to be initialized Some(unsafe { self.back_maybe_uninit().assume_init_ref() }) } /// Returns a mutable reference to the back element, or `None` if the buffer is empty. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// assert_eq!(buf.back_mut(), None); /// /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// match buf.back_mut() { /// None => (), /// Some(x) => *x = 'z', /// } /// assert_eq!(buf, ['a', 'b', 'z']); /// ``` #[inline] pub fn back_mut(&mut self) -> Option<&mut T> { if N == 0 || self.size == 0 { // Nothing to do return None; } // SAFETY: `size` is non-zero; back element is guaranteed to be initialized Some(unsafe { self.back_maybe_uninit_mut().assume_init_mut() }) } /// Returns a reference to the front element, or `None` if the buffer is empty. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// assert_eq!(buf.front(), None); /// /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// assert_eq!(buf.front(), Some(&'a')); /// ``` #[inline] pub fn front(&self) -> Option<&T> { if N == 0 || self.size == 0 { // Nothing to do return None; } // SAFETY: `size` is non-zero; front element is guaranteed to be initialized Some(unsafe { self.front_maybe_uninit().assume_init_ref() }) } /// Returns a mutable reference to the front element, or `None` if the buffer is empty. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// assert_eq!(buf.front_mut(), None); /// /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// match buf.front_mut() { /// None => (), /// Some(x) => *x = 'z', /// } /// assert_eq!(buf, ['z', 'b', 'c']); /// ``` #[inline] pub fn front_mut(&mut self) -> Option<&mut T> { if N == 0 || self.size == 0 { // Nothing to do return None; } // SAFETY: `size` is non-zero; front element is guaranteed to be initialized Some(unsafe { self.front_maybe_uninit_mut().assume_init_mut() }) } /// Returns a reference to the element at the given index, or `None` if the element does not /// exist. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// assert_eq!(buf.get(1), None); /// /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// assert_eq!(buf.get(1), Some(&'b')); /// ``` #[inline] pub fn get(&self, index: usize) -> Option<&T> { if N == 0 || index >= self.size { // Nothing to do return None; } // SAFETY: `index` is in a valid range; it is guaranteed to point to an initialized element Some(unsafe { self.get_maybe_uninit(index).assume_init_ref() }) } /// Returns a mutable reference to the element at the given index, or `None` if the element /// does not exist. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, char>::new(); /// assert_eq!(buf.get_mut(1), None); /// /// buf.push_back('a'); /// buf.push_back('b'); /// buf.push_back('c'); /// match buf.get_mut(1) { /// None => (), /// Some(x) => *x = 'z', /// } /// assert_eq!(buf, ['a', 'z', 'c']); /// ``` #[inline] pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { if N == 0 || index >= self.size { // Nothing to do return None; } // SAFETY: `index` is in a valid range; it is guaranteed to point to an initialized element Some(unsafe { self.get_maybe_uninit_mut(index).assume_init_mut() }) } /// Appends an element to the back of the buffer. /// /// If the buffer is full, the element at the front of the buffer is overwritten and returned. /// /// See also [`try_push_back()`](CircularBuffer::try_push_back) for a non-overwriting version /// of this method. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<3, char>::new(); /// /// assert_eq!(buf.push_back('a'), None); /// assert_eq!(buf, ['a']); /// /// assert_eq!(buf.push_back('b'), None); /// assert_eq!(buf, ['a', 'b']); /// /// assert_eq!(buf.push_back('c'), None); /// assert_eq!(buf, ['a', 'b', 'c']); /// /// // The buffer is now full; adding more values causes the front elements to be removed and /// // returned /// assert_eq!(buf.push_back('d'), Some('a')); /// assert_eq!(buf, ['b', 'c', 'd']); /// /// assert_eq!(buf.push_back('e'), Some('b')); /// assert_eq!(buf, ['c', 'd', 'e']); /// /// assert_eq!(buf.push_back('f'), Some('c')); /// assert_eq!(buf, ['d', 'e', 'f']); /// ``` pub fn push_back(&mut self, item: T) -> Option { if N == 0 { // Nothing to do return Some(item); } if self.size >= N { // At capacity; need to replace the front item // // SAFETY: if size is greater than 0, the front item is guaranteed to be initialized. let replaced_item = mem::replace( unsafe { self.front_maybe_uninit_mut().assume_init_mut() }, item, ); self.inc_start(); Some(replaced_item) } else { // Some uninitialized slots left; append at the end self.inc_size(); self.back_maybe_uninit_mut().write(item); None } } /// Appends an element to the back of the buffer. /// /// If the buffer is full, the buffer is not modified and the given element is returned as an /// error. /// /// See also [`push_back()`](CircularBuffer::push_back) for a version of this method that /// overwrites the front of the buffer when full. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<3, char>::new(); /// /// assert_eq!(buf.try_push_back('a'), Ok(())); /// assert_eq!(buf, ['a']); /// /// assert_eq!(buf.try_push_back('b'), Ok(())); /// assert_eq!(buf, ['a', 'b']); /// /// assert_eq!(buf.try_push_back('c'), Ok(())); /// assert_eq!(buf, ['a', 'b', 'c']); /// /// // The buffer is now full; adding more values results in an error /// assert_eq!(buf.try_push_back('d'), Err('d')) /// ``` pub fn try_push_back(&mut self, item: T) -> Result<(), T> { if N == 0 { // Nothing to do return Ok(()); } if self.size >= N { // At capacity; return the pushed item as error Err(item) } else { // Some uninitialized slots left; append at the end self.inc_size(); self.back_maybe_uninit_mut().write(item); Ok(()) } } /// Appends an element to the front of the buffer. /// /// If the buffer is full, the element at the back of the buffer is overwritten and returned. /// /// See also [`try_push_front()`](CircularBuffer::try_push_front) for a non-overwriting version /// of this method. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<3, char>::new(); /// /// assert_eq!(buf.push_front('a'), None); /// assert_eq!(buf, ['a']); /// /// assert_eq!(buf.push_front('b'), None); /// assert_eq!(buf, ['b', 'a']); /// /// assert_eq!(buf.push_front('c'), None); /// assert_eq!(buf, ['c', 'b', 'a']); /// /// // The buffer is now full; adding more values causes the back elements to be dropped /// assert_eq!(buf.push_front('d'), Some('a')); /// assert_eq!(buf, ['d', 'c', 'b']); /// /// assert_eq!(buf.push_front('e'), Some('b')); /// assert_eq!(buf, ['e', 'd', 'c']); /// /// assert_eq!(buf.push_front('f'), Some('c')); /// assert_eq!(buf, ['f', 'e', 'd']); /// ``` pub fn push_front(&mut self, item: T) -> Option { if N == 0 { // Nothing to do return Some(item); } if self.size >= N { // At capacity; need to replace the back item // // SAFETY: if size is greater than 0, the back item is guaranteed to be initialized. let replaced_item = mem::replace( unsafe { self.back_maybe_uninit_mut().assume_init_mut() }, item, ); self.dec_start(); Some(replaced_item) } else { // Some uninitialized slots left; insert at the start self.inc_size(); self.dec_start(); self.front_maybe_uninit_mut().write(item); None } } /// Appends an element to the front of the buffer. /// /// If the buffer is full, the buffer is not modified and the given element is returned as an /// error. /// /// See also [`push_front()`](CircularBuffer::push_front) for a version of this method that /// overwrites the back of the buffer when full. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<3, char>::new(); /// /// assert_eq!(buf.try_push_front('a'), Ok(())); /// assert_eq!(buf, ['a']); /// /// assert_eq!(buf.try_push_front('b'), Ok(())); /// assert_eq!(buf, ['b', 'a']); /// /// assert_eq!(buf.try_push_front('c'), Ok(())); /// assert_eq!(buf, ['c', 'b', 'a']); /// /// // The buffer is now full; adding more values results in an error /// assert_eq!(buf.try_push_front('d'), Err('d')); /// ``` pub fn try_push_front(&mut self, item: T) -> Result<(), T> { if N == 0 { // Nothing to do return Ok(()); } if self.size >= N { // At capacity; return the pushed item as error Err(item) } else { // Some uninitialized slots left; insert at the start self.inc_size(); self.dec_start(); self.front_maybe_uninit_mut().write(item); Ok(()) } } /// Removes and returns an element from the back of the buffer. /// /// If the buffer is empty, `None` is returned. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<3, char>::from(['a', 'b', 'c']); /// /// assert_eq!(buf.pop_back(), Some('c')); /// assert_eq!(buf.pop_back(), Some('b')); /// assert_eq!(buf.pop_back(), Some('a')); /// assert_eq!(buf.pop_back(), None); /// ``` pub fn pop_back(&mut self) -> Option { if N == 0 || self.size == 0 { // Nothing to do return None; } // SAFETY: if size is greater than 0, the back item is guaranteed to be initialized. let back = unsafe { self.back_maybe_uninit().assume_init_read() }; self.dec_size(); Some(back) } /// Removes and returns an element from the front of the buffer. /// /// If the buffer is empty, `None` is returned. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<3, char>::from(['a', 'b', 'c']); /// /// assert_eq!(buf.pop_front(), Some('a')); /// assert_eq!(buf.pop_front(), Some('b')); /// assert_eq!(buf.pop_front(), Some('c')); /// assert_eq!(buf.pop_front(), None); /// ``` pub fn pop_front(&mut self) -> Option { if N == 0 || self.size == 0 { // Nothing to do return None; } // SAFETY: if size is greater than 0, the front item is guaranteed to be initialized. let front = unsafe { self.front_maybe_uninit().assume_init_read() }; self.dec_size(); self.inc_start(); Some(front) } /// Removes and returns an element at the specified index. /// /// If the index is out of bounds, `None` is returned. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<3, char>::from(['a', 'b', 'c']); /// /// assert_eq!(buf.remove(1), Some('b')); /// assert_eq!(buf, ['a', 'c']); /// /// assert_eq!(buf.remove(5), None); /// ``` pub fn remove(&mut self, index: usize) -> Option { if N == 0 || index >= self.size { return None; } let index = add_mod(self.start, index, N); let back_index = add_mod(self.start, self.size - 1, N); // SAFETY: `index` is in a valid range; the element is guaranteed to be initialized let item = unsafe { self.items[index].assume_init_read() }; // SAFETY: the pointers being moved are in a valid range; the elements behind those // pointers are guaranteed to be initialized unsafe { // TODO: optimize for the case where `index < len - index` (i.e. when copying items to // the right is cheaper than moving items to the left) let ptr = self.items.as_mut_ptr(); if back_index >= index { // Move the values at the right of `index` by 1 position to the left ptr::copy(ptr.add(index).add(1), ptr.add(index), back_index - index); } else { // Move the values at the right of `index` by 1 position to the left ptr::copy(ptr.add(index).add(1), ptr.add(index), N - index - 1); // Move the leftmost value to the end of the array ptr::copy(ptr, ptr.add(N - 1), 1); // Move the values at the left of `back_index` by 1 position to the left ptr::copy(ptr.add(1), ptr, back_index); } } self.dec_size(); Some(item) } /// Swap the element at index `i` with the element at index `j`. /// /// # Panics /// /// If either `i` or `j` is out of bounds. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<5, char>::from(['a', 'b', 'c', 'd']); /// assert_eq!(buf, ['a', 'b', 'c', 'd']); /// /// buf.swap(0, 3); /// assert_eq!(buf, ['d', 'b', 'c', 'a']); /// ``` /// /// Trying to swap an invalid index panics: /// /// ```should_panic /// use circular_buffer::CircularBuffer; /// let mut buf = CircularBuffer::<5, char>::from(['a', 'b', 'c', 'd']); /// buf.swap(0, 7); /// ``` pub fn swap(&mut self, i: usize, j: usize) { assert!(i < self.size, "i index out-of-bounds"); assert!(j < self.size, "j index out-of-bounds"); if i != j { let i = add_mod(self.start, i, N); let j = add_mod(self.start, j, N); // SAFETY: these are valid pointers unsafe { ptr::swap_nonoverlapping(&mut self.items[i], &mut self.items[j], 1) }; } } /// Removes the element at `index` and returns it, replacing it with the back of the buffer. /// /// Returns `None` if `index` is out-of-bounds. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<5, char>::from(['a', 'b', 'c', 'd']); /// assert_eq!(buf, ['a', 'b', 'c', 'd']); /// /// assert_eq!(buf.swap_remove_back(2), Some('c')); /// assert_eq!(buf, ['a', 'b', 'd']); /// /// assert_eq!(buf.swap_remove_back(7), None); /// ``` pub fn swap_remove_back(&mut self, index: usize) -> Option { if index >= self.size { return None; } self.swap(index, self.size - 1); self.pop_back() } /// Removes the element at `index` and returns it, replacing it with the front of the buffer. /// /// Returns `None` if `index` is out-of-bounds. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<5, char>::from(['a', 'b', 'c', 'd']); /// assert_eq!(buf, ['a', 'b', 'c', 'd']); /// /// assert_eq!(buf.swap_remove_front(2), Some('c')); /// assert_eq!(buf, ['b', 'a', 'd']); /// /// assert_eq!(buf.swap_remove_front(7), None); /// ``` pub fn swap_remove_front(&mut self, index: usize) -> Option { if index >= self.size { return None; } self.swap(index, 0); self.pop_front() } /// Fills the entire capacity of `self` with elements by cloning `value`. /// /// The elements already present in the buffer (if any) are all replaced by clones of `value`, /// and the spare capacity of the buffer is also filled with clones of `value`. /// /// This is equivalent to clearing the buffer and adding clones of `value` until reaching the /// maximum capacity. /// /// If you want to replace only the existing elements of the buffer, without affecting the /// spare capacity, use [`as_mut_slices()`](CircularBuffer::as_mut_slices) and call /// [`slice::fill()`]([]::fill) on the resulting slices. /// /// See also: [`fill_with()`](CircularBuffer::fill_with), /// [`fill_spare()`](CircularBuffer::fill_spare), /// [`fill_spare_with()`](CircularBuffer::fill_spare_with). /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<10, u32>::from([1, 2, 3]); /// assert_eq!(buf, [1, 2, 3]); /// /// buf.fill(9); /// assert_eq!(buf, [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]); /// ``` /// /// If you want to replace existing elements only: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<10, u32>::from([1, 2, 3]); /// assert_eq!(buf, [1, 2, 3]); /// /// let (front, back) = buf.as_mut_slices(); /// front.fill(9); /// back.fill(9); /// assert_eq!(buf, [9, 9, 9]); /// ``` pub fn fill(&mut self, value: T) where T: Clone, { self.clear(); self.fill_spare(value); } /// Fills the entire capacity of `self` with elements by calling a closure. /// /// The elements already present in the buffer (if any) are all replaced by the result of the /// closure, and the spare capacity of the buffer is also filled with the result of the /// closure. /// /// This is equivalent to clearing the buffer and adding the result of the closure until /// reaching the maximum capacity. /// /// If you want to replace only the existing elements of the buffer, without affecting the /// spare capacity, use [`as_mut_slices()`](CircularBuffer::as_mut_slices) and call /// [`slice::fill_with()`]([]::fill_with) on the resulting slices. /// /// See also: [`fill()`](CircularBuffer::fill), [`fill_spare()`](CircularBuffer::fill_spare), /// [`fill_spare_with()`](CircularBuffer::fill_spare_with). /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<10, u32>::from([1, 2, 3]); /// assert_eq!(buf, [1, 2, 3]); /// /// let mut x = 2; /// buf.fill_with(|| { /// x *= 2; /// x /// }); /// assert_eq!(buf, [4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]); /// ``` /// /// If you want to replace existing elements only: /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<10, u32>::from([1, 2, 3]); /// assert_eq!(buf, [1, 2, 3]); /// /// let mut x = 2; /// let (front, back) = buf.as_mut_slices(); /// front.fill_with(|| { /// x *= 2; /// x /// }); /// back.fill_with(|| { /// x *= 2; /// x /// }); /// assert_eq!(buf, [4, 8, 16]); /// ``` pub fn fill_with(&mut self, f: F) where F: FnMut() -> T, { self.clear(); self.fill_spare_with(f); } /// Fills the spare capacity of `self` with elements by cloning `value`. /// /// The elements already present in the buffer (if any) are unaffected. /// /// This is equivalent to adding clones of `value` to the buffer until reaching the maximum /// capacity. /// /// See also: [`fill()`](CircularBuffer::fill), [`fill_with()`](CircularBuffer::fill_with), /// [`fill_spare_with()`](CircularBuffer::fill_spare_with). /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<10, u32>::from([1, 2, 3]); /// assert_eq!(buf, [1, 2, 3]); /// /// buf.fill_spare(9); /// assert_eq!(buf, [1, 2, 3, 9, 9, 9, 9, 9, 9, 9]); /// ``` pub fn fill_spare(&mut self, value: T) where T: Clone, { if N == 0 || self.size == N { return; } // TODO Optimize while self.size < N - 1 { self.push_back(value.clone()); } self.push_back(value); } /// Fills the spare capacity of `self` with elements by calling a closure. /// /// The elements already present in the buffer (if any) are unaffected. /// /// This is equivalent adding the result of the closure to the buffer until reaching the /// maximum capacity. /// /// See also: [`fill()`](CircularBuffer::fill), [`fill_with()`](CircularBuffer::fill_with), /// [`fill_spare()`](CircularBuffer::fill_spare). /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<10, u32>::from([1, 2, 3]); /// assert_eq!(buf, [1, 2, 3]); /// /// let mut x = 2; /// buf.fill_spare_with(|| { /// x *= 2; /// x /// }); /// assert_eq!(buf, [1, 2, 3, 4, 8, 16, 32, 64, 128, 256]); /// ``` pub fn fill_spare_with(&mut self, mut f: F) where F: FnMut() -> T, { if N == 0 { return; } // TODO Optimize while self.size < N { self.push_back(f()); } } /// Shortens the buffer, keeping only the front `len` elements and dropping the rest. /// /// If `len` is equal or greater to the buffer's current length, this has no effect. /// /// Calling `truncate_back(0)` is equivalent to [`clear()`](Self::clear). /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, u32>::from([10, 20, 30]); /// /// buf.truncate_back(1); /// assert_eq!(buf, [10]); /// /// // Truncating to a length that is greater than the buffer's length has no effect /// buf.truncate_back(8); /// assert_eq!(buf, [10]); /// ``` pub fn truncate_back(&mut self, len: usize) { if N == 0 || len >= self.size { // Nothing to do return; } let drop_range = len..self.size; // SAFETY: `drop_range` is a valid range, so elements within are guaranteed to be // initialized. The `size` of the buffer is shrunk before dropping, so no value will be // dropped twice in case of panics. unsafe { self.drop_range(drop_range) }; self.size = len; } /// Shortens the buffer, keeping only the back `len` elements and dropping the rest. /// /// If `len` is equal or greater to the buffer's current length, this has no effect. /// /// Calling `truncate_front(0)` is equivalent to [`clear()`](Self::clear). /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, u32>::from([10, 20, 30]); /// /// buf.truncate_front(1); /// assert_eq!(buf, [30]); /// /// // Truncating to a length that is greater than the buffer's length has no effect /// buf.truncate_front(8); /// assert_eq!(buf, [30]); /// ``` pub fn truncate_front(&mut self, len: usize) { if N == 0 || len >= self.size { // Nothing to do return; } let drop_len = self.size - len; let drop_range = 0..drop_len; // SAFETY: `drop_range` is a valid range, so elements within are guaranteed to be // initialized. The `start` of the buffer is shrunk before dropping, so no value will be // dropped twice in case of panics. unsafe { self.drop_range(drop_range) }; self.start = add_mod(self.start, drop_len, N); self.size = len; } /// Drops all the elements in the buffer. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf = CircularBuffer::<4, u32>::from([10, 20, 30]); /// assert_eq!(buf, [10, 20, 30]); /// buf.clear(); /// assert_eq!(buf, []); /// ``` #[inline] pub fn clear(&mut self) { self.truncate_back(0) } } impl CircularBuffer where T: Clone, { /// Clones and appends all the elements from the slice to the back of the buffer. /// /// This is an optimized version of [`extend()`](CircularBuffer::extend) for slices. /// /// If slice contains more values than the available capacity, the elements at the front of the /// buffer are dropped. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let mut buf: CircularBuffer<5, u32> = CircularBuffer::from([1, 2, 3]); /// buf.extend_from_slice(&[4, 5, 6, 7]); /// assert_eq!(buf, [3, 4, 5, 6, 7]); /// ``` pub fn extend_from_slice(&mut self, other: &[T]) { if N == 0 { return; } debug_assert!(self.start < N, "start out-of-bounds"); debug_assert!(self.size <= N, "size out-of-bounds"); #[cfg(not(feature = "unstable"))] fn write_uninit_slice_cloned(dst: &mut [MaybeUninit], src: &[T]) { // Each call to `clone()` may panic, therefore we need to track how many elements we // successfully cloned so that we can drop them in case of panic. This `Guard` struct // does exactly that: it keeps track of how many items have been successfully cloned // and drops them if the guard is dropped. // // This implementation was highly inspired by the implementation of // `MaybeUninit::clone_from_slice` struct Guard<'a, T> { dst: &'a mut [MaybeUninit], initialized: usize, } impl Drop for Guard<'_, T> { fn drop(&mut self) { let initialized = &mut self.dst[..self.initialized]; // SAFETY: this slice contain only initialized objects; `MaybeUninit` has // the same alignment and size as `T` unsafe { let initialized = &mut *(initialized as *mut [MaybeUninit] as *mut [T]); ptr::drop_in_place(initialized); } } } debug_assert_eq!(dst.len(), src.len()); let len = dst.len(); let mut guard = Guard { dst, initialized: 0, }; #[allow(clippy::needless_range_loop)] for i in 0..len { guard.dst[i].write(src[i].clone()); guard.initialized += 1; } // All the `clone()` calls succeded; get rid of the guard without running its `drop()` // implementation mem::forget(guard); } if other.len() < N { // All the elements of `other` fit into the buffer let free_size = N - self.size; let final_size = if other.len() < free_size { // All the elements of `other` fit at the back of the buffer self.size + other.len() } else { // Some of the elements of `other` need to overwrite the front of the buffer self.truncate_front(N - other.len()); N }; let (right, left) = self.slices_uninit_mut(); let write_len = core::cmp::min(right.len(), other.len()); #[cfg(feature = "unstable")] MaybeUninit::clone_from_slice(&mut right[..write_len], &other[..write_len]); #[cfg(not(feature = "unstable"))] write_uninit_slice_cloned(&mut right[..write_len], &other[..write_len]); let other = &other[write_len..]; debug_assert!(left.len() >= other.len()); let write_len = other.len(); #[cfg(feature = "unstable")] MaybeUninit::clone_from_slice(&mut left[..write_len], other); #[cfg(not(feature = "unstable"))] write_uninit_slice_cloned(&mut left[..write_len], other); self.size = final_size; } else { // `other` overwrites the whole buffer; get only the last `N` elements from `other` and // overwrite self.clear(); self.start = 0; let other = &other[other.len() - N..]; debug_assert_eq!(self.items.len(), other.len()); #[cfg(feature = "unstable")] MaybeUninit::clone_from_slice(&mut self.items, other); #[cfg(not(feature = "unstable"))] write_uninit_slice_cloned(&mut self.items, other); self.size = N; } } /// Clones the elements of the buffer into a new [`Vec`], leaving the buffer unchanged. /// /// # Examples /// /// ``` /// use circular_buffer::CircularBuffer; /// /// let buf: CircularBuffer<5, u32> = CircularBuffer::from([1, 2, 3]); /// let vec: Vec = buf.to_vec(); /// /// assert_eq!(buf, [1, 2, 3]); /// assert_eq!(vec, [1, 2, 3]); /// ``` #[must_use] #[cfg(feature = "alloc")] pub fn to_vec(&self) -> Vec { let mut vec = Vec::with_capacity(self.size); vec.extend(self.iter().cloned()); debug_assert_eq!(vec.len(), self.size); vec } } impl Default for CircularBuffer { #[inline] fn default() -> Self { Self::new() } } impl From<[T; M]> for CircularBuffer { fn from(mut arr: [T; M]) -> Self { #[cfg(feature = "unstable")] let mut elems = MaybeUninit::::uninit_array(); #[cfg(not(feature = "unstable"))] let mut elems = unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() }; let arr_ptr = &arr as *const T as *const MaybeUninit; let elems_ptr = &mut elems as *mut MaybeUninit; let size = if N >= M { M } else { N }; // SAFETY: // - `M - size` is non-negative, and `arr_ptr.add(M - size)` points to a memory location // that contains exactly `size` elements // - `elems_ptr` points to a memory location that contains exactly `N` elements, and `N` is // greater than or equal to `size` unsafe { ptr::copy_nonoverlapping(arr_ptr.add(M - size), elems_ptr, size); } // Prevent destructors from running on those elements that we've taken ownership of; only // destroy the elements that were discareded // // SAFETY: All elements in `arr` are initialized; `forget` will make sure that destructors // are not run twice unsafe { ptr::drop_in_place(&mut arr[..M - size]); } mem::forget(arr); Self { size, start: 0, items: elems, } } } impl FromIterator for CircularBuffer { fn from_iter(iter: I) -> Self where I: IntoIterator, { // TODO Optimize let mut buf = Self::new(); iter.into_iter().for_each(|item| { buf.push_back(item); }); buf } } impl Extend for CircularBuffer { fn extend(&mut self, iter: I) where I: IntoIterator, { // TODO Optimize iter.into_iter().for_each(|item| { self.push_back(item); }); } } impl<'a, const N: usize, T> Extend<&'a T> for CircularBuffer where T: Copy, { fn extend(&mut self, iter: I) where I: IntoIterator, { // TODO Optimize iter.into_iter().for_each(|item| { self.push_back(*item); }); } } impl Index for CircularBuffer { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { self.get(index).expect("index out-of-bounds") } } impl IndexMut for CircularBuffer { #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { self.get_mut(index).expect("index out-of-bounds") } } impl IntoIterator for CircularBuffer { type Item = T; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter::new(self) } } impl<'a, const N: usize, T> IntoIterator for &'a CircularBuffer { type Item = &'a T; type IntoIter = Iter<'a, T>; #[inline] fn into_iter(self) -> Self::IntoIter { Iter::new(self) } } impl PartialEq> for CircularBuffer where T: PartialEq, { fn eq(&self, other: &CircularBuffer) -> bool { if self.len() != other.len() { return false; } let (a_left, a_right) = self.as_slices(); let (b_left, b_right) = other.as_slices(); match a_left.len().cmp(&b_left.len()) { Ordering::Less => { let x = a_left.len(); let y = b_left.len() - x; a_left[..] == b_left[..x] && a_right[..y] == b_left[x..] && a_right[y..] == b_right[..] } Ordering::Greater => { let x = b_left.len(); let y = a_left.len() - x; a_left[..x] == b_left[..] && a_left[x..] == b_right[..y] && a_right[..] == b_right[y..] } Ordering::Equal => { debug_assert_eq!(a_left.len(), b_left.len()); debug_assert_eq!(a_right.len(), b_right.len()); a_left == b_left && a_right == b_right } } } } impl Eq for CircularBuffer where T: Eq {} impl PartialEq<[U]> for CircularBuffer where T: PartialEq, { fn eq(&self, other: &[U]) -> bool { if self.len() != other.len() { return false; } let (a_left, a_right) = self.as_slices(); let (b_left, b_right) = other.split_at(a_left.len()); debug_assert_eq!(a_left.len(), b_left.len()); debug_assert_eq!(a_right.len(), b_right.len()); a_left == b_left && a_right == b_right } } impl PartialEq<[U; M]> for CircularBuffer where T: PartialEq, { #[inline] fn eq(&self, other: &[U; M]) -> bool { self == &other[..] } } impl<'a, const N: usize, T, U> PartialEq<&'a [U]> for CircularBuffer where T: PartialEq, { #[inline] fn eq(&self, other: &&'a [U]) -> bool { self == *other } } impl<'a, const N: usize, T, U> PartialEq<&'a mut [U]> for CircularBuffer where T: PartialEq, { #[inline] fn eq(&self, other: &&'a mut [U]) -> bool { self == *other } } impl<'a, const N: usize, const M: usize, T, U> PartialEq<&'a [U; M]> for CircularBuffer where T: PartialEq, { #[inline] fn eq(&self, other: &&'a [U; M]) -> bool { self == *other } } impl<'a, const N: usize, const M: usize, T, U> PartialEq<&'a mut [U; M]> for CircularBuffer where T: PartialEq, { #[inline] fn eq(&self, other: &&'a mut [U; M]) -> bool { self == *other } } impl PartialOrd> for CircularBuffer where T: PartialOrd, { fn partial_cmp(&self, other: &CircularBuffer) -> Option { self.iter().partial_cmp(other.iter()) } } impl Ord for CircularBuffer where T: Ord, { fn cmp(&self, other: &Self) -> Ordering { self.iter().cmp(other.iter()) } } impl Hash for CircularBuffer where T: Hash, { fn hash(&self, state: &mut H) { self.size.hash(state); self.iter().for_each(|item| item.hash(state)); } } impl Clone for CircularBuffer where T: Clone, { fn clone(&self) -> Self { // TODO Optimize Self::from_iter(self.iter().cloned()) } fn clone_from(&mut self, other: &Self) { // TODO Optimize self.clear(); self.extend(other.iter().cloned()); } } impl Drop for CircularBuffer { #[inline] fn drop(&mut self) { // `clear()` will make sure that every element is dropped in a safe way self.clear(); } } impl fmt::Debug for CircularBuffer where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self).finish() } } circular-buffer-1.0.0/src/tests.rs000064400000000000000000002022051046102023000151750ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause #![allow(static_mut_refs)] #![cfg(feature = "std")] use crate::CircularBuffer; use drop_tracker::DropItem; use drop_tracker::DropTracker; use std::cell::RefCell; use std::collections::hash_map::DefaultHasher; use std::hash::Hash; use std::hash::Hasher; use std::io::BufRead; use std::io::Read; use std::io::Write; use std::ops::Bound; fn is_contiguous(buf: &CircularBuffer) -> bool { let slices = buf.as_slices(); slices.1.is_empty() } macro_rules! assert_buf_eq { ( $buf:ident , [] $( as [ $( $arrtyp:tt )* ] )? ) => { let expected = [] $(as [ $($arrtyp)* ])?; assert!($buf.is_empty(), "{} is not empty", stringify!($buf)); assert_eq!($buf, expected, "{} does not equal an empty array", stringify!($buf)); assert_eq!($buf, &expected, "{} does not equal an empty array reference", stringify!($buf)); assert_eq!($buf, &expected[..], "{} does not equal an empty slice", stringify!($buf)); assert_eq!($buf.len(), 0, "{} length does not equal 0", stringify!($buf)); assert_eq!($buf.iter().next(), None, "{} iterator is not empty", stringify!($buf)); }; ( $buf:ident , [ $( $elems:tt )* ] ) => { let expected = [ $($elems)* ]; assert!(!$buf.is_empty(), "{} is empty", stringify!($buf)); assert_eq!($buf, expected); assert_eq!($buf, &expected); assert_eq!($buf, &expected[..]); assert_eq!($buf.len(), expected.len(), "{} length does not equal {}", stringify!($buf), expected.len()); let mut buf_iter = $buf.iter(); let mut expected_iter = expected.iter(); loop { match (buf_iter.next(), expected_iter.next()) { (Some(buf_item), Some(expected_item)) => assert_eq!(buf_item, expected_item), (Some(buf_item), None) => panic!("{} iterator returned extra item: {:?}", stringify!(buf), buf_item), (None, Some(expected_item)) => panic!("{} iterator missing item: {:?}", stringify!(buf), expected_item), (None, None) => break, } } for i in 0..expected.len() { assert_eq!($buf[i], expected[i]); } }; } macro_rules! assert_buf_slices_eq { ( $buf:ident , [ $( $front_elems:tt )* ] , [ $( $back_elems:tt )* ] ) => { let mut expected_front = [ $($front_elems)* ]; let mut expected_back = [ $($back_elems)* ]; assert_eq!($buf.as_slices(), (&expected_front[..], &expected_back[..])); assert_eq!($buf.as_mut_slices(), (&mut expected_front[..], &mut expected_back[..])); } } #[test] fn attrs() { let mut buf = CircularBuffer::<4, u32>::new(); assert_eq!(buf.len(), 0); assert!(buf.is_empty()); assert!(!buf.is_full()); buf.push_back(1); assert_eq!(buf.len(), 1); assert!(!buf.is_empty()); assert!(!buf.is_full()); buf.push_back(2); assert_eq!(buf.len(), 2); assert!(!buf.is_empty()); assert!(!buf.is_full()); buf.push_back(3); assert_eq!(buf.len(), 3); assert!(!buf.is_empty()); assert!(!buf.is_full()); buf.push_back(4); assert_eq!(buf.len(), 4); assert!(!buf.is_empty()); assert!(buf.is_full()); buf.push_back(5); assert_eq!(buf.len(), 4); assert!(!buf.is_empty()); assert!(buf.is_full()); buf.push_back(6); assert_eq!(buf.len(), 4); assert!(!buf.is_empty()); assert!(buf.is_full()); buf.clear(); assert_eq!(buf.len(), 0); assert!(buf.is_empty()); assert!(!buf.is_full()); } #[test] fn push_back() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); assert_eq!(buf.push_back(tracker.track(1)), None); assert_buf_eq!(buf, [1]); tracker.assert_all_alive([1]); tracker.assert_fully_alive(); assert_eq!(buf.push_back(tracker.track(2)), None); assert_buf_eq!(buf, [1, 2]); tracker.assert_all_alive([1, 2]); tracker.assert_fully_alive(); assert_eq!(buf.push_back(tracker.track(3)), None); assert_buf_eq!(buf, [1, 2, 3]); tracker.assert_all_alive([1, 2, 3]); tracker.assert_fully_alive(); assert_eq!(buf.push_back(tracker.track(4)), None); assert_buf_eq!(buf, [1, 2, 3, 4]); tracker.assert_all_alive([1, 2, 3, 4]); tracker.assert_fully_alive(); assert_eq!(buf.push_back(tracker.track(5)).unwrap(), 1); assert_buf_eq!(buf, [2, 3, 4, 5]); tracker.assert_all_alive([2, 3, 4, 5]); tracker.assert_all_dropped([1]); assert_eq!(buf.push_back(tracker.track(6)).unwrap(), 2); assert_buf_eq!(buf, [3, 4, 5, 6]); tracker.assert_all_alive([3, 4, 5, 6]); tracker.assert_all_dropped([1, 2]); assert_eq!(buf.push_back(tracker.track(7)).unwrap(), 3); assert_buf_eq!(buf, [4, 5, 6, 7]); tracker.assert_all_alive([4, 5, 6, 7]); tracker.assert_all_dropped([1, 2, 3]); assert_eq!(buf.push_back(tracker.track(8)).unwrap(), 4); assert_buf_eq!(buf, [5, 6, 7, 8]); tracker.assert_all_alive([5, 6, 7, 8]); tracker.assert_all_dropped([1, 2, 3, 4]); } #[test] fn push_front() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); assert_eq!(buf.push_front(tracker.track(1)), None); assert_buf_eq!(buf, [1]); tracker.assert_all_alive([1]); tracker.assert_fully_alive(); assert_eq!(buf.push_front(tracker.track(2)), None); assert_buf_eq!(buf, [2, 1]); tracker.assert_all_alive([1, 2]); tracker.assert_fully_alive(); assert_eq!(buf.push_front(tracker.track(3)), None); assert_buf_eq!(buf, [3, 2, 1]); tracker.assert_all_alive([1, 2, 3]); tracker.assert_fully_alive(); assert_eq!(buf.push_front(tracker.track(4)), None); assert_buf_eq!(buf, [4, 3, 2, 1]); tracker.assert_all_alive([1, 2, 3, 4]); tracker.assert_fully_alive(); assert_eq!(buf.push_front(tracker.track(5)).unwrap(), 1); assert_buf_eq!(buf, [5, 4, 3, 2]); tracker.assert_all_alive([2, 3, 4, 5]); tracker.assert_all_dropped([1]); assert_eq!(buf.push_front(tracker.track(6)).unwrap(), 2); assert_buf_eq!(buf, [6, 5, 4, 3]); tracker.assert_all_alive([3, 4, 5, 6]); tracker.assert_all_dropped([1, 2]); assert_eq!(buf.push_front(tracker.track(7)).unwrap(), 3); assert_buf_eq!(buf, [7, 6, 5, 4]); tracker.assert_all_alive([4, 5, 6, 7]); tracker.assert_all_dropped([1, 2, 3]); assert_eq!(buf.push_front(tracker.track(8)).unwrap(), 4); assert_buf_eq!(buf, [8, 7, 6, 5]); tracker.assert_all_alive([5, 6, 7, 8]); tracker.assert_all_dropped([1, 2, 3, 4]); } #[test] fn pop_back() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_back(), None); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_back(1); assert_buf_eq!(buf, [1]); assert_eq!(buf.pop_back(), Some(1)); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_back(), None); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_back(2); assert_buf_eq!(buf, [2]); buf.push_back(3); assert_buf_eq!(buf, [2, 3]); assert_eq!(buf.pop_back(), Some(3)); assert_buf_eq!(buf, [2]); assert_eq!(buf.pop_back(), Some(2)); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_back(), None); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_back(4); assert_buf_eq!(buf, [4]); buf.push_back(5); assert_buf_eq!(buf, [4, 5]); buf.push_back(6); assert_buf_eq!(buf, [4, 5, 6]); buf.push_back(7); assert_buf_eq!(buf, [4, 5, 6, 7]); buf.push_back(8); assert_buf_eq!(buf, [5, 6, 7, 8]); assert_eq!(buf.pop_back(), Some(8)); assert_buf_eq!(buf, [5, 6, 7]); assert_eq!(buf.pop_back(), Some(7)); assert_buf_eq!(buf, [5, 6]); assert_eq!(buf.pop_back(), Some(6)); assert_buf_eq!(buf, [5]); assert_eq!(buf.pop_back(), Some(5)); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_back(), None); assert_buf_eq!(buf, [] as [u32; 0]); } #[test] fn pop_front() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_front(), None); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_front(1); assert_buf_eq!(buf, [1]); assert_eq!(buf.pop_front(), Some(1)); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_front(), None); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_front(2); assert_buf_eq!(buf, [2]); buf.push_front(3); assert_buf_eq!(buf, [3, 2]); assert_eq!(buf.pop_front(), Some(3)); assert_buf_eq!(buf, [2]); assert_eq!(buf.pop_front(), Some(2)); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_front(), None); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_front(4); assert_buf_eq!(buf, [4]); buf.push_front(5); assert_buf_eq!(buf, [5, 4]); buf.push_front(6); assert_buf_eq!(buf, [6, 5, 4]); buf.push_front(7); assert_buf_eq!(buf, [7, 6, 5, 4]); buf.push_front(8); assert_buf_eq!(buf, [8, 7, 6, 5]); assert_eq!(buf.pop_front(), Some(8)); assert_buf_eq!(buf, [7, 6, 5]); assert_eq!(buf.pop_front(), Some(7)); assert_buf_eq!(buf, [6, 5]); assert_eq!(buf.pop_front(), Some(6)); assert_buf_eq!(buf, [5]); assert_eq!(buf.pop_front(), Some(5)); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.pop_front(), None); assert_buf_eq!(buf, [] as [u32; 0]); } #[test] fn remove() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.remove(0), None); assert_eq!(buf.remove(1), None); assert_eq!(buf.remove(2), None); assert_eq!(buf.remove(3), None); assert_eq!(buf.remove(4), None); assert_eq!(buf.remove(5), None); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_back(1); assert_buf_eq!(buf, [1]); assert_eq!(buf.remove(0), Some(1)); assert_buf_eq!(buf, [] as [u32; 0]); buf.push_back(2); assert_buf_eq!(buf, [2]); buf.push_back(3); assert_buf_eq!(buf, [2, 3]); buf.push_back(4); assert_buf_eq!(buf, [2, 3, 4]); assert_eq!(buf.remove(1), Some(3)); assert_buf_eq!(buf, [2, 4]); buf.push_back(5); assert_buf_eq!(buf, [2, 4, 5]); buf.push_back(6); assert_buf_eq!(buf, [2, 4, 5, 6]); buf.push_back(7); assert_buf_eq!(buf, [4, 5, 6, 7]); buf.push_back(8); assert_buf_eq!(buf, [5, 6, 7, 8]); assert_eq!(buf.remove(2), Some(7)); assert_buf_eq!(buf, [5, 6, 8]); assert_eq!(buf.remove(2), Some(8)); assert_buf_eq!(buf, [5, 6]); assert_eq!(buf.remove(1), Some(6)); assert_buf_eq!(buf, [5]); assert_eq!(buf.remove(0), Some(5)); assert_buf_eq!(buf, [] as [u32; 0]); } #[test] fn truncate_back() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); buf.push_back(tracker.track(1)); buf.push_back(tracker.track(2)); buf.push_back(tracker.track(3)); buf.push_back(tracker.track(4)); assert_buf_eq!(buf, [1, 2, 3, 4]); tracker.assert_all_alive([1, 2, 3, 4]); tracker.assert_fully_alive(); buf.truncate_back(2); assert_buf_eq!(buf, [1, 2]); tracker.assert_all_alive([1, 2]); tracker.assert_all_dropped([3, 4]); buf.truncate_back(0); assert_buf_eq!(buf, [] as [i32; 0]); tracker.assert_fully_dropped(); tracker.assert_all_dropped([1, 2, 3, 4]); // Explicitly drop `buf` here to avoid an early implicit drop drop(buf); } #[test] fn truncate_front() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); buf.push_back(tracker.track(1)); buf.push_back(tracker.track(2)); buf.push_back(tracker.track(3)); buf.push_back(tracker.track(4)); assert_buf_eq!(buf, [1, 2, 3, 4]); tracker.assert_all_alive([1, 2, 3, 4]); tracker.assert_fully_alive(); buf.truncate_front(2); assert_buf_eq!(buf, [3, 4]); tracker.assert_all_alive([3, 4]); tracker.assert_all_dropped([1, 2]); buf.truncate_front(0); assert_buf_eq!(buf, [] as [i32; 0]); tracker.assert_fully_dropped(); tracker.assert_all_dropped([1, 2, 3, 4]); // Explicitly drop `buf` here to avoid an early implicit drop drop(buf); } #[test] fn get() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(buf.get(0), None); assert_eq!(buf.get(1), None); assert_eq!(buf.get(2), None); assert_eq!(buf.get(3), None); assert_eq!(buf.get(4), None); assert_eq!(buf.get(5), None); buf.push_back(1); assert_buf_eq!(buf, [1]); assert_eq!(buf.get(0), Some(&1)); assert_eq!(buf.get(1), None); buf.push_back(2); assert_buf_eq!(buf, [1, 2]); assert_eq!(buf.get(0), Some(&1)); assert_eq!(buf.get(1), Some(&2)); assert_eq!(buf.get(2), None); buf.push_back(3); assert_buf_eq!(buf, [1, 2, 3]); assert_eq!(buf.get(0), Some(&1)); assert_eq!(buf.get(1), Some(&2)); assert_eq!(buf.get(2), Some(&3)); assert_eq!(buf.get(3), None); buf.push_back(4); assert_buf_eq!(buf, [1, 2, 3, 4]); assert_eq!(buf.get(0), Some(&1)); assert_eq!(buf.get(1), Some(&2)); assert_eq!(buf.get(2), Some(&3)); assert_eq!(buf.get(3), Some(&4)); assert_eq!(buf.get(4), None); buf.push_back(5); assert_buf_eq!(buf, [2, 3, 4, 5]); assert_eq!(buf.get(0), Some(&2)); assert_eq!(buf.get(1), Some(&3)); assert_eq!(buf.get(2), Some(&4)); assert_eq!(buf.get(3), Some(&5)); assert_eq!(buf.get(4), None); buf.push_back(6); assert_buf_eq!(buf, [3, 4, 5, 6]); assert_eq!(buf.get(0), Some(&3)); assert_eq!(buf.get(1), Some(&4)); assert_eq!(buf.get(2), Some(&5)); assert_eq!(buf.get(3), Some(&6)); assert_eq!(buf.get(5), None); buf.push_back(7); assert_buf_eq!(buf, [4, 5, 6, 7]); assert_eq!(buf.get(0), Some(&4)); assert_eq!(buf.get(1), Some(&5)); assert_eq!(buf.get(2), Some(&6)); assert_eq!(buf.get(3), Some(&7)); assert_eq!(buf.get(4), None); buf.push_back(8); assert_buf_eq!(buf, [5, 6, 7, 8]); assert_eq!(buf.get(0), Some(&5)); assert_eq!(buf.get(1), Some(&6)); assert_eq!(buf.get(2), Some(&7)); assert_eq!(buf.get(3), Some(&8)); assert_eq!(buf.get(4), None); } #[test] fn index() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); assert!(std::panic::catch_unwind(|| buf[0]).is_err()); assert!(std::panic::catch_unwind(|| buf[1]).is_err()); assert!(std::panic::catch_unwind(|| buf[2]).is_err()); assert!(std::panic::catch_unwind(|| buf[3]).is_err()); assert!(std::panic::catch_unwind(|| buf[4]).is_err()); assert!(std::panic::catch_unwind(|| buf[5]).is_err()); buf.push_back(1); assert_buf_eq!(buf, [1]); assert_eq!(buf[0], 1); assert!(std::panic::catch_unwind(|| buf[1]).is_err()); buf.push_back(2); assert_buf_eq!(buf, [1, 2]); assert_eq!(buf[0], 1); assert_eq!(buf[1], 2); assert!(std::panic::catch_unwind(|| buf[2]).is_err()); buf.push_back(3); assert_buf_eq!(buf, [1, 2, 3]); assert_eq!(buf[0], 1); assert_eq!(buf[1], 2); assert_eq!(buf[2], 3); assert!(std::panic::catch_unwind(|| buf[3]).is_err()); buf.push_back(4); assert_buf_eq!(buf, [1, 2, 3, 4]); assert_eq!(buf[0], 1); assert_eq!(buf[1], 2); assert_eq!(buf[2], 3); assert_eq!(buf[3], 4); assert!(std::panic::catch_unwind(|| buf[4]).is_err()); buf.push_back(5); assert_buf_eq!(buf, [2, 3, 4, 5]); assert_eq!(buf[0], 2); assert_eq!(buf[1], 3); assert_eq!(buf[2], 4); assert_eq!(buf[3], 5); assert!(std::panic::catch_unwind(|| buf[4]).is_err()); buf.push_back(6); assert_buf_eq!(buf, [3, 4, 5, 6]); assert_eq!(buf[0], 3); assert_eq!(buf[1], 4); assert_eq!(buf[2], 5); assert_eq!(buf[3], 6); assert!(std::panic::catch_unwind(|| buf[4]).is_err()); buf.push_back(7); assert_buf_eq!(buf, [4, 5, 6, 7]); assert_eq!(buf[0], 4); assert_eq!(buf[1], 5); assert_eq!(buf[2], 6); assert_eq!(buf[3], 7); assert!(std::panic::catch_unwind(|| buf[4]).is_err()); buf.push_back(8); assert_buf_eq!(buf, [5, 6, 7, 8]); assert_eq!(buf[0], 5); assert_eq!(buf[1], 6); assert_eq!(buf[2], 7); assert_eq!(buf[3], 8); assert!(std::panic::catch_unwind(|| buf[4]).is_err()); } #[test] fn iter() { let buf = CircularBuffer::<8, u32>::new(); let mut iter = buf.iter(); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4]); let mut iter = buf.iter(); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); assert_eq!(iter.next(), Some(&3)); assert_eq!(iter.next(), Some(&4)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4, 5, 6, 7, 8]); let mut iter = buf.iter(); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); assert_eq!(iter.next(), Some(&3)); assert_eq!(iter.next(), Some(&4)); assert_eq!(iter.next(), Some(&5)); assert_eq!(iter.next(), Some(&6)); assert_eq!(iter.next(), Some(&7)); assert_eq!(iter.next(), Some(&8)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn iter_rev() { let buf = CircularBuffer::<8, u32>::new(); let mut iter = buf.iter().rev(); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4]); let mut iter = buf.iter().rev(); assert_eq!(iter.next(), Some(&4)); assert_eq!(iter.next(), Some(&3)); assert_eq!(iter.next(), Some(&2)); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4, 5, 6, 7, 8]); let mut iter = buf.iter().rev(); assert_eq!(iter.next(), Some(&8)); assert_eq!(iter.next(), Some(&7)); assert_eq!(iter.next(), Some(&6)); assert_eq!(iter.next(), Some(&5)); assert_eq!(iter.next(), Some(&4)); assert_eq!(iter.next(), Some(&3)); assert_eq!(iter.next(), Some(&2)); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn into_iter() { let buf = CircularBuffer::<8, u32>::new(); let mut iter = buf.into_iter(); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4]); let mut iter = buf.into_iter(); assert_eq!(iter.next(), Some(1)); assert_eq!(iter.next(), Some(2)); assert_eq!(iter.next(), Some(3)); assert_eq!(iter.next(), Some(4)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4, 5, 6, 7, 8]); let mut iter = buf.into_iter(); assert_eq!(iter.next(), Some(1)); assert_eq!(iter.next(), Some(2)); assert_eq!(iter.next(), Some(3)); assert_eq!(iter.next(), Some(4)); assert_eq!(iter.next(), Some(5)); assert_eq!(iter.next(), Some(6)); assert_eq!(iter.next(), Some(7)); assert_eq!(iter.next(), Some(8)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn into_iter_rev() { let buf = CircularBuffer::<8, u32>::new(); let mut iter = buf.into_iter().rev(); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4]); let mut iter = buf.into_iter().rev(); assert_eq!(iter.next(), Some(4)); assert_eq!(iter.next(), Some(3)); assert_eq!(iter.next(), Some(2)); assert_eq!(iter.next(), Some(1)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4, 5, 6, 7, 8]); let mut iter = buf.into_iter().rev(); assert_eq!(iter.next(), Some(8)); assert_eq!(iter.next(), Some(7)); assert_eq!(iter.next(), Some(6)); assert_eq!(iter.next(), Some(5)); assert_eq!(iter.next(), Some(4)); assert_eq!(iter.next(), Some(3)); assert_eq!(iter.next(), Some(2)); assert_eq!(iter.next(), Some(1)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn iter_mut() { let mut buf = CircularBuffer::<8, u32>::new(); let mut iter = buf.iter_mut(); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4]); let mut iter = buf.iter_mut(); assert_eq!(iter.next(), Some(&mut 1)); assert_eq!(iter.next(), Some(&mut 2)); assert_eq!(iter.next(), Some(&mut 3)); assert_eq!(iter.next(), Some(&mut 4)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4, 5, 6, 7, 8]); let mut iter = buf.iter_mut(); assert_eq!(iter.next(), Some(&mut 1)); assert_eq!(iter.next(), Some(&mut 2)); assert_eq!(iter.next(), Some(&mut 3)); assert_eq!(iter.next(), Some(&mut 4)); assert_eq!(iter.next(), Some(&mut 5)); assert_eq!(iter.next(), Some(&mut 6)); assert_eq!(iter.next(), Some(&mut 7)); assert_eq!(iter.next(), Some(&mut 8)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn iter_mut_rev() { let mut buf = CircularBuffer::<8, u32>::new(); let mut iter = buf.iter_mut().rev(); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4]); let mut iter = buf.iter_mut().rev(); assert_eq!(iter.next(), Some(&mut 4)); assert_eq!(iter.next(), Some(&mut 3)); assert_eq!(iter.next(), Some(&mut 2)); assert_eq!(iter.next(), Some(&mut 1)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, u32>::from([1, 2, 3, 4, 5, 6, 7, 8]); let mut iter = buf.iter_mut().rev(); assert_eq!(iter.next(), Some(&mut 8)); assert_eq!(iter.next(), Some(&mut 7)); assert_eq!(iter.next(), Some(&mut 6)); assert_eq!(iter.next(), Some(&mut 5)); assert_eq!(iter.next(), Some(&mut 4)); assert_eq!(iter.next(), Some(&mut 3)); assert_eq!(iter.next(), Some(&mut 2)); assert_eq!(iter.next(), Some(&mut 1)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn range() { let buf = CircularBuffer::<8, char>::new(); let mut iter = buf.range(..); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range(..); assert_eq!(iter.next(), Some(&'a')); assert_eq!(iter.next(), Some(&'b')); assert_eq!(iter.next(), Some(&'c')); assert_eq!(iter.next(), Some(&'d')); assert_eq!(iter.next(), Some(&'e')); assert_eq!(iter.next(), Some(&'f')); assert_eq!(iter.next(), Some(&'g')); assert_eq!(iter.next(), Some(&'h')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range(5..); assert_eq!(iter.next(), Some(&'f')); assert_eq!(iter.next(), Some(&'g')); assert_eq!(iter.next(), Some(&'h')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range(..3); assert_eq!(iter.next(), Some(&'a')); assert_eq!(iter.next(), Some(&'b')); assert_eq!(iter.next(), Some(&'c')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range(..=2); assert_eq!(iter.next(), Some(&'a')); assert_eq!(iter.next(), Some(&'b')); assert_eq!(iter.next(), Some(&'c')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range(3..6); assert_eq!(iter.next(), Some(&'d')); assert_eq!(iter.next(), Some(&'e')); assert_eq!(iter.next(), Some(&'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range(3..=5); assert_eq!(iter.next(), Some(&'d')); assert_eq!(iter.next(), Some(&'e')); assert_eq!(iter.next(), Some(&'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range(0..0); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range((Bound::Excluded(4), Bound::Unbounded)); assert_eq!(iter.next(), Some(&'f')); assert_eq!(iter.next(), Some(&'g')); assert_eq!(iter.next(), Some(&'h')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range((Bound::Excluded(2), Bound::Excluded(6))); assert_eq!(iter.next(), Some(&'d')); assert_eq!(iter.next(), Some(&'e')); assert_eq!(iter.next(), Some(&'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range((Bound::Excluded(2), Bound::Included(5))); assert_eq!(iter.next(), Some(&'d')); assert_eq!(iter.next(), Some(&'e')); assert_eq!(iter.next(), Some(&'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range((Bound::Excluded(2), Bound::Excluded(3))); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range((Bound::Excluded(2), Bound::Included(2))); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let buf = CircularBuffer::<8, char>::from_iter("abcdefghijkl".chars()); let mut iter = buf.range(2..6); assert_eq!(iter.next(), Some(&'g')); assert_eq!(iter.next(), Some(&'h')); assert_eq!(iter.next(), Some(&'i')); assert_eq!(iter.next(), Some(&'j')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn range_mut() { let mut buf = CircularBuffer::<8, char>::new(); let mut iter = buf.range_mut(..); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut(..); assert_eq!(iter.next(), Some(&mut 'a')); assert_eq!(iter.next(), Some(&mut 'b')); assert_eq!(iter.next(), Some(&mut 'c')); assert_eq!(iter.next(), Some(&mut 'd')); assert_eq!(iter.next(), Some(&mut 'e')); assert_eq!(iter.next(), Some(&mut 'f')); assert_eq!(iter.next(), Some(&mut 'g')); assert_eq!(iter.next(), Some(&mut 'h')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut(5..); assert_eq!(iter.next(), Some(&mut 'f')); assert_eq!(iter.next(), Some(&mut 'g')); assert_eq!(iter.next(), Some(&mut 'h')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut(..3); assert_eq!(iter.next(), Some(&mut 'a')); assert_eq!(iter.next(), Some(&mut 'b')); assert_eq!(iter.next(), Some(&mut 'c')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut(..=2); assert_eq!(iter.next(), Some(&mut 'a')); assert_eq!(iter.next(), Some(&mut 'b')); assert_eq!(iter.next(), Some(&mut 'c')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut(3..6); assert_eq!(iter.next(), Some(&mut 'd')); assert_eq!(iter.next(), Some(&mut 'e')); assert_eq!(iter.next(), Some(&mut 'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut(3..=5); assert_eq!(iter.next(), Some(&mut 'd')); assert_eq!(iter.next(), Some(&mut 'e')); assert_eq!(iter.next(), Some(&mut 'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut(0..0); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut((Bound::Excluded(4), Bound::Unbounded)); assert_eq!(iter.next(), Some(&mut 'f')); assert_eq!(iter.next(), Some(&mut 'g')); assert_eq!(iter.next(), Some(&mut 'h')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut((Bound::Excluded(2), Bound::Excluded(6))); assert_eq!(iter.next(), Some(&mut 'd')); assert_eq!(iter.next(), Some(&mut 'e')); assert_eq!(iter.next(), Some(&mut 'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut((Bound::Excluded(2), Bound::Included(5))); assert_eq!(iter.next(), Some(&mut 'd')); assert_eq!(iter.next(), Some(&mut 'e')); assert_eq!(iter.next(), Some(&mut 'f')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut((Bound::Excluded(2), Bound::Excluded(3))); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefgh".chars()); let mut iter = buf.range_mut((Bound::Excluded(2), Bound::Included(2))); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); let mut buf = CircularBuffer::<8, char>::from_iter("abcdefghijkl".chars()); let mut iter = buf.range_mut(2..6); assert_eq!(iter.next(), Some(&mut 'g')); assert_eq!(iter.next(), Some(&mut 'h')); assert_eq!(iter.next(), Some(&mut 'i')); assert_eq!(iter.next(), Some(&mut 'j')); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn zero_capacity() { fn run_assertions(buf: &CircularBuffer<0, u32>) { assert_eq!(*buf, []); assert_eq!(buf.len(), 0); assert_eq!(buf.capacity(), 0); assert_eq!(buf.to_vec(), []); assert!(buf.is_empty()); assert!(buf.is_full()); } let mut buf = CircularBuffer::<0, u32>::new(); run_assertions(&buf); buf.push_back(1); run_assertions(&buf); assert_eq!(buf.pop_back(), None); run_assertions(&buf); buf.push_front(1); run_assertions(&buf); assert_eq!(buf.pop_front(), None); run_assertions(&buf); assert_eq!(buf.remove(0), None); run_assertions(&buf); buf.extend(&[1, 2, 3]); run_assertions(&buf); buf.extend_from_slice(&[1, 2, 3]); run_assertions(&buf); buf.truncate_back(10); run_assertions(&buf); buf.truncate_back(0); run_assertions(&buf); buf.truncate_front(10); run_assertions(&buf); buf.truncate_front(0); run_assertions(&buf); buf.clear(); run_assertions(&buf); } #[test] fn remove_on_empty() { fn run_assertions(buf: &CircularBuffer<10, u32>) { assert_eq!(*buf, []); assert_eq!(buf.len(), 0); assert_eq!(buf.to_vec(), []); assert!(buf.is_empty()); } let mut buf = CircularBuffer::<10, u32>::new(); run_assertions(&buf); assert_eq!(buf.pop_back(), None); run_assertions(&buf); assert_eq!(buf.pop_front(), None); run_assertions(&buf); assert_eq!(buf.remove(0), None); run_assertions(&buf); buf.truncate_back(10); run_assertions(&buf); buf.truncate_back(0); run_assertions(&buf); buf.truncate_front(10); run_assertions(&buf); buf.truncate_front(0); run_assertions(&buf); buf.clear(); run_assertions(&buf); } #[test] fn swap() { let mut buf: CircularBuffer<4, u32> = [1, 2, 3, 4].into_iter().collect(); buf.swap(0, 3); assert_buf_eq!(buf, [4, 2, 3, 1]); buf.swap(1, 2); assert_buf_eq!(buf, [4, 3, 2, 1]); buf.pop_front(); assert_buf_eq!(buf, [3, 2, 1]); buf.push_back(4); assert_buf_eq!(buf, [3, 2, 1, 4]); assert!(!is_contiguous(&buf)); buf.swap(0, 1); assert_buf_eq!(buf, [2, 3, 1, 4]); buf.swap(1, 2); assert_buf_eq!(buf, [2, 1, 3, 4]); buf.swap(2, 3); assert_buf_eq!(buf, [2, 1, 4, 3]); buf.swap(3, 0); assert_buf_eq!(buf, [3, 1, 4, 2]); } #[test] fn drop_contiguous() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); buf.push_back(tracker.track(1)); buf.push_back(tracker.track(2)); assert_buf_eq!(buf, [1, 2]); assert!(is_contiguous(&buf)); tracker.assert_all_alive([1, 2]); tracker.assert_fully_alive(); drop(buf); tracker.assert_fully_dropped(); tracker.assert_all_dropped([1, 2]); } #[test] fn drop_full_contiguous() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); buf.push_back(tracker.track(1)); buf.push_back(tracker.track(2)); buf.push_back(tracker.track(3)); buf.push_back(tracker.track(4)); assert_buf_eq!(buf, [1, 2, 3, 4]); assert!(is_contiguous(&buf)); tracker.assert_all_alive([1, 2, 3, 4]); tracker.assert_fully_alive(); drop(buf); tracker.assert_fully_dropped(); tracker.assert_all_dropped([1, 2, 3, 4]); } #[test] fn drop_full_disjoint() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); buf.push_back(tracker.track(1)); buf.push_back(tracker.track(2)); buf.push_back(tracker.track(3)); buf.push_back(tracker.track(4)); buf.push_back(tracker.track(5)); buf.push_back(tracker.track(6)); assert_buf_eq!(buf, [3, 4, 5, 6]); assert!(!is_contiguous(&buf)); tracker.assert_all_alive([3, 4, 5, 6]); tracker.assert_all_dropped([1, 2]); drop(buf); tracker.assert_fully_dropped(); tracker.assert_all_dropped([1, 2, 3, 4, 5, 6]); } #[test] fn drop_disjoint() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<4, DropItem>::new(); assert_buf_eq!(buf, [] as [i32; 0]); buf.push_back(tracker.track(1)); buf.push_back(tracker.track(2)); buf.push_back(tracker.track(3)); buf.push_back(tracker.track(4)); buf.push_back(tracker.track(5)); buf.push_back(tracker.track(6)); buf.pop_back(); assert_buf_eq!(buf, [3, 4, 5]); assert!(!is_contiguous(&buf)); tracker.assert_all_alive([3, 4, 5]); tracker.assert_all_dropped([1, 2, 6]); drop(buf); tracker.assert_fully_dropped(); tracker.assert_all_dropped([1, 2, 3, 4, 5, 6]); } #[test] fn drain_front() { // Fully consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); let mut drain = buf.drain(..4); assert_eq!(drain.next().unwrap(), 1); assert_eq!(drain.next().unwrap(), 2); assert_eq!(drain.next().unwrap(), 3); assert_eq!(drain.next().unwrap(), 4); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); drop(drain); assert_buf_eq!(buf, [5, 6, 7]); tracker.assert_all_alive([5, 6, 7]); tracker.assert_all_dropped([1, 2, 3, 4]); // Partially consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); let mut drain = buf.drain(..4); assert_eq!(drain.next().unwrap(), 1); assert_eq!(drain.next().unwrap(), 2); drop(drain); assert_buf_eq!(buf, [5, 6, 7]); tracker.assert_all_alive([5, 6, 7]); tracker.assert_all_dropped([1, 2, 3, 4]); // Do not consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); let _ = buf.drain(..4); assert_buf_eq!(buf, [5, 6, 7]); tracker.assert_all_alive([5, 6, 7]); tracker.assert_all_dropped([1, 2, 3, 4]); } #[test] fn drain_back() { // Fully consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); let mut drain = buf.drain(3..); assert_eq!(drain.next().unwrap(), 4); assert_eq!(drain.next().unwrap(), 5); assert_eq!(drain.next().unwrap(), 6); assert_eq!(drain.next().unwrap(), 7); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); drop(drain); assert_buf_eq!(buf, [1, 2, 3]); tracker.assert_all_alive([1, 2, 3]); tracker.assert_all_dropped([4, 5, 6, 7]); // Partially consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); let mut drain = buf.drain(3..); assert_eq!(drain.next().unwrap(), 4); assert_eq!(drain.next().unwrap(), 5); drop(drain); assert_buf_eq!(buf, [1, 2, 3]); tracker.assert_all_alive([1, 2, 3]); tracker.assert_all_dropped([4, 5, 6, 7]); // Do not consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); let _ = buf.drain(3..); assert_buf_eq!(buf, [1, 2, 3]); tracker.assert_all_alive([1, 2, 3]); tracker.assert_all_dropped([4, 5, 6, 7]); } #[test] fn drain_middle_contiguous() { // Fully consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); assert!(is_contiguous(&buf)); let mut drain = buf.drain(2..5); assert_eq!(drain.next().unwrap(), 3); assert_eq!(drain.next().unwrap(), 4); assert_eq!(drain.next().unwrap(), 5); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); drop(drain); assert_buf_eq!(buf, [1, 2, 6, 7]); tracker.assert_all_alive([1, 2, 6, 7]); tracker.assert_all_dropped([3, 4, 5]); // Partially consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); assert!(is_contiguous(&buf)); let mut drain = buf.drain(2..5); assert_eq!(drain.next().unwrap(), 3); assert_eq!(drain.next().unwrap(), 4); drop(drain); assert_buf_eq!(buf, [1, 2, 6, 7]); tracker.assert_all_alive([1, 2, 6, 7]); tracker.assert_all_dropped([3, 4, 5]); // Do not consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4, 5, 6, 7])); assert!(is_contiguous(&buf)); let _ = buf.drain(2..5); assert_buf_eq!(buf, [1, 2, 6, 7]); tracker.assert_all_alive([1, 2, 6, 7]); tracker.assert_all_dropped([3, 4, 5]); } #[test] fn drain_middle_disjoint() { // Fully consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter( tracker.track_many([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), ); assert!(!is_contiguous(&buf)); let mut drain = buf.drain(3..7); assert_eq!(drain.next().unwrap(), 9); assert_eq!(drain.next().unwrap(), 10); assert_eq!(drain.next().unwrap(), 11); assert_eq!(drain.next().unwrap(), 12); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); drop(drain); assert_buf_eq!(buf, [6, 7, 8, 13, 14, 15]); tracker.assert_all_alive([6, 7, 8, 13, 14, 15]); tracker.assert_all_dropped([9, 10, 11, 12]); // Partially consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter( tracker.track_many([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), ); assert!(!is_contiguous(&buf)); let mut drain = buf.drain(3..7); assert_eq!(drain.next().unwrap(), 9); assert_eq!(drain.next().unwrap(), 10); drop(drain); assert_buf_eq!(buf, [6, 7, 8, 13, 14, 15]); tracker.assert_all_alive([6, 7, 8, 13, 14, 15]); tracker.assert_all_dropped([9, 10, 11, 12]); // Do not consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter( tracker.track_many([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), ); assert!(!is_contiguous(&buf)); let _ = buf.drain(3..7); assert_buf_eq!(buf, [6, 7, 8, 13, 14, 15]); tracker.assert_all_alive([6, 7, 8, 13, 14, 15]); tracker.assert_all_dropped([9, 10, 11, 12]); } #[test] fn drain_full() { // Fully consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4])); let mut drain = buf.drain(..); assert_eq!(drain.next().unwrap(), 1); assert_eq!(drain.next().unwrap(), 2); assert_eq!(drain.next().unwrap(), 3); assert_eq!(drain.next().unwrap(), 4); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); drop(drain); assert_buf_eq!(buf, [] as [i32; 0]); tracker.assert_fully_dropped(); // Partially consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4])); let mut drain = buf.drain(..); assert_eq!(drain.next().unwrap(), 1); assert_eq!(drain.next().unwrap(), 2); drop(drain); assert_buf_eq!(buf, [] as [i32; 0]); tracker.assert_fully_dropped(); // Do not consume the drain let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4])); let _ = buf.drain(..); assert_buf_eq!(buf, [] as [i32; 0]); tracker.assert_fully_dropped(); } #[test] fn drain_empty() { let mut tracker = DropTracker::new(); let mut buf = CircularBuffer::<10, _>::from_iter(tracker.track_many([1, 2, 3, 4])); let mut drain = buf.drain(0..0); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); assert_eq!(drain.next(), None); drop(drain); assert_buf_eq!(buf, [1, 2, 3, 4]); tracker.assert_fully_alive(); } #[test] fn eq_contiguous() { let mut buf1 = CircularBuffer::<5, _>::from_iter([1, 2, 3]); let mut buf2 = CircularBuffer::<5, _>::from_iter([1, 2, 3]); assert!(is_contiguous(&buf1)); assert!(is_contiguous(&buf2)); assert_eq!(buf1, buf2); buf1.push_back(4); assert!(is_contiguous(&buf1)); assert!(is_contiguous(&buf2)); assert_ne!(buf1, buf2); buf2.push_back(4); assert!(is_contiguous(&buf1)); assert!(is_contiguous(&buf2)); assert_eq!(buf1, buf2); } #[test] fn eq_disjoint() { let mut buf1 = CircularBuffer::<5, _>::from_iter([1, 2, 3, 4, 5]); let mut buf2 = CircularBuffer::<5, _>::from_iter([0, 1, 2, 3, 4]); buf1.push_back(6); buf2.push_back(5); assert!(!is_contiguous(&buf1)); assert!(!is_contiguous(&buf2)); assert_ne!(buf1, buf2); buf2.push_back(6); assert!(!is_contiguous(&buf1)); assert!(!is_contiguous(&buf2)); assert_eq!(buf1, buf2); buf1.push_back(7); buf2.push_back(7); assert!(!is_contiguous(&buf1)); assert!(!is_contiguous(&buf2)); assert_eq!(buf1, buf2); buf1.push_back(8); buf2.push_back(8); assert!(!is_contiguous(&buf1)); assert!(!is_contiguous(&buf2)); assert_eq!(buf1, buf2); buf1.push_back(9); buf2.push_back(9); assert!(!is_contiguous(&buf1)); assert!(is_contiguous(&buf2)); assert_eq!(buf1, buf2); buf1.push_back(10); buf2.push_back(10); assert!(is_contiguous(&buf1)); assert!(!is_contiguous(&buf2)); assert_eq!(buf1, buf2); } #[test] fn write() { let mut buf = CircularBuffer::<4, u8>::new(); assert_buf_eq!(buf, [] as [u8; 0]); assert!(write!(&mut buf, "hello").is_ok()); assert_buf_eq!(buf, [b'e', b'l', b'l', b'o']); assert!(write!(&mut buf, "world").is_ok()); assert_buf_eq!(buf, [b'o', b'r', b'l', b'd']); } #[test] fn read() { fn read_all(mut buf: R) -> Vec { let mut vec = Vec::new(); buf.read_to_end(&mut vec).expect("read failed"); vec } let mut buf = CircularBuffer::<4, u8>::new(); assert_buf_eq!(buf, [] as [u8; 0]); assert_eq!(read_all(&mut buf), []); assert_buf_eq!(buf, [] as [u8; 0]); buf.push_back(b'a'); buf.push_back(b'b'); assert_buf_eq!(buf, [b'a', b'b']); assert_eq!(read_all(&mut buf), [b'a', b'b']); assert_buf_eq!(buf, [] as [u8; 0]); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_buf_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(read_all(&mut buf), [b'c', b'd', b'e', b'f']); assert_buf_eq!(buf, [] as [u8; 0]); } #[test] fn read_buf() { let mut buf = CircularBuffer::<4, u8>::new(); assert_buf_eq!(buf, [] as [u8; 0]); assert_eq!(buf.fill_buf().unwrap(), b""); buf.push_back(b'a'); buf.push_back(b'b'); assert_buf_eq!(buf, [b'a', b'b']); assert_eq!(buf.fill_buf().unwrap(), b"ab"); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_buf_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(buf.fill_buf().unwrap(), b"cd"); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b"ef"); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b""); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b""); } #[test] fn from_array() { let arr = []; let buf = CircularBuffer::<4, i32>::from(arr); assert_buf_eq!(buf, [] as [i32; 0]); let mut tracker = DropTracker::new(); let arr = [tracker.track(1), tracker.track(2)]; let buf = CircularBuffer::<4, DropItem>::from(arr); assert_buf_eq!(buf, [1, 2]); tracker.assert_all_alive([1, 2]); tracker.assert_fully_alive(); let mut tracker = DropTracker::new(); let arr = [ tracker.track(1), tracker.track(2), tracker.track(3), tracker.track(4), ]; let buf = CircularBuffer::<4, DropItem>::from(arr); assert_buf_eq!(buf, [1, 2, 3, 4]); tracker.assert_all_alive([1, 2, 3, 4]); tracker.assert_fully_alive(); let mut tracker = DropTracker::new(); let arr = [ tracker.track(1), tracker.track(2), tracker.track(3), tracker.track(4), tracker.track(5), tracker.track(6), ]; let buf = CircularBuffer::<4, DropItem>::from(arr); assert_buf_eq!(buf, [3, 4, 5, 6]); tracker.assert_all_alive([3, 4, 5, 6]); tracker.assert_all_dropped([1, 2]); let mut tracker = DropTracker::new(); let arr = [ tracker.track(1), tracker.track(2), tracker.track(3), tracker.track(4), tracker.track(5), tracker.track(6), tracker.track(7), tracker.track(8), ]; let buf = CircularBuffer::<4, DropItem>::from(arr); assert_buf_eq!(buf, [5, 6, 7, 8]); tracker.assert_all_alive([5, 6, 7, 8]); tracker.assert_all_dropped([1, 2, 3, 4]); } #[test] fn from_iter() { let vec = vec![]; let buf = CircularBuffer::<4, i32>::from_iter(vec); assert_buf_eq!(buf, [] as [i32; 0]); let mut tracker = DropTracker::new(); let vec = vec![tracker.track(1), tracker.track(2)]; let buf = CircularBuffer::<4, DropItem>::from_iter(vec); assert_buf_eq!(buf, [1, 2]); tracker.assert_all_alive([1, 2]); tracker.assert_fully_alive(); let mut tracker = DropTracker::new(); let vec = vec![ tracker.track(1), tracker.track(2), tracker.track(3), tracker.track(4), ]; let buf = CircularBuffer::<4, DropItem>::from_iter(vec); assert_buf_eq!(buf, [1, 2, 3, 4]); tracker.assert_all_alive([1, 2, 3, 4]); tracker.assert_fully_alive(); let mut tracker = DropTracker::new(); let vec = vec![ tracker.track(1), tracker.track(2), tracker.track(3), tracker.track(4), tracker.track(5), tracker.track(6), ]; let buf = CircularBuffer::<4, DropItem>::from_iter(vec); assert_buf_eq!(buf, [3, 4, 5, 6]); tracker.assert_all_alive([3, 4, 5, 6]); tracker.assert_all_dropped([1, 2]); let mut tracker = DropTracker::new(); let vec = vec![ tracker.track(1), tracker.track(2), tracker.track(3), tracker.track(4), tracker.track(5), tracker.track(6), tracker.track(7), tracker.track(8), ]; let buf = CircularBuffer::<4, DropItem>::from_iter(vec); assert_buf_eq!(buf, [5, 6, 7, 8]); tracker.assert_all_alive([5, 6, 7, 8]); tracker.assert_all_dropped([1, 2, 3, 4]); } #[test] fn extend() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); buf.extend([] as [u32; 0]); assert_buf_eq!(buf, [] as [u32; 0]); buf.extend([1]); assert_buf_eq!(buf, [1]); buf.extend([2, 3]); assert_buf_eq!(buf, [1, 2, 3]); buf.extend([4, 5, 6]); assert_buf_eq!(buf, [3, 4, 5, 6]); buf.extend([7, 8, 9, 10]); assert_buf_eq!(buf, [7, 8, 9, 10]); buf.extend([11, 12, 13, 14, 15]); assert_buf_eq!(buf, [12, 13, 14, 15]); } #[test] fn extend_ref() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); buf.extend([].iter()); assert_buf_eq!(buf, [] as [u32; 0]); buf.extend([1].iter()); assert_buf_eq!(buf, [1]); buf.extend([2, 3].iter()); assert_buf_eq!(buf, [1, 2, 3]); buf.extend([4, 5, 6].iter()); assert_buf_eq!(buf, [3, 4, 5, 6]); buf.extend([7, 8, 9, 10].iter()); assert_buf_eq!(buf, [7, 8, 9, 10]); buf.extend([11, 12, 13, 14, 15].iter()); assert_buf_eq!(buf, [12, 13, 14, 15]); } #[test] fn extend_from_slice() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); buf.extend_from_slice(&[][..]); assert_buf_eq!(buf, [] as [u32; 0]); buf.extend_from_slice(&[1][..]); assert_buf_eq!(buf, [1]); buf.extend_from_slice(&[2, 3][..]); assert_buf_eq!(buf, [1, 2, 3]); buf.extend_from_slice(&[4, 5, 6][..]); assert_buf_eq!(buf, [3, 4, 5, 6]); buf.extend_from_slice(&[7, 8, 9, 10][..]); assert_buf_eq!(buf, [7, 8, 9, 10]); buf.extend_from_slice(&[11, 12, 13, 14, 15][..]); assert_buf_eq!(buf, [12, 13, 14, 15]); } #[test] fn extend_from_slice_unwind_safety() { // This needs to be `static` to be used in `fn clone()` below static mut TRACKER: Option> = None; // SAFETY: the assumption is that this test function will be called only once unsafe { TRACKER.replace(DropTracker::new()); } fn tracker() -> &'static DropTracker { unsafe { TRACKER.as_ref().unwrap() } } fn tracker_mut() -> &'static mut DropTracker { unsafe { TRACKER.as_mut().unwrap() } } #[derive(PartialEq, Eq, Hash)] struct FaultyClonable { drop_item: DropItem, panic_on_clone: bool, } impl Clone for FaultyClonable { fn clone(&self) -> Self { if self.panic_on_clone { panic!("clone failed :("); } else { Self { drop_item: tracker_mut().track(format!("clone of {}", self.drop_item)), panic_on_clone: false, } } } } let array = [ FaultyClonable { drop_item: tracker_mut().track("a".to_string()), panic_on_clone: false, }, FaultyClonable { drop_item: tracker_mut().track("b".to_string()), panic_on_clone: false, }, FaultyClonable { drop_item: tracker_mut().track("c".to_string()), panic_on_clone: true, }, FaultyClonable { drop_item: tracker_mut().track("d".to_string()), panic_on_clone: false, }, ]; let mut buf = CircularBuffer::<4, FaultyClonable>::new(); let res = std::panic::catch_unwind(move || buf.extend_from_slice(&array)); assert!(res.is_err()); tracker().assert_dropped("clone of a"); tracker().assert_dropped("clone of b"); assert!(!tracker().is_tracked("clone of c")); assert!(!tracker().is_tracked("clone of d")); } #[test] fn fill_efficiency() { #[derive(Default, Debug)] struct Cloneable { num_clones: RefCell, is_clone: bool, } impl Clone for Cloneable { fn clone(&self) -> Self { if self.is_clone { panic!("cannot create clone of another clone"); } *self.num_clones.borrow_mut() += 1; Self { num_clones: RefCell::default(), is_clone: true, } } } let mut buf = CircularBuffer::<6, Cloneable>::new(); buf.fill(Cloneable::default()); assert_eq!(buf.len(), buf.capacity()); // The last element should be the original `value` that we passed. As such, the number of // clones created should be `len - 1` assert_eq!(*buf.back().unwrap().num_clones.borrow(), buf.len() - 1); // Only the last element should have been cloned; the other elements should have not been // cloned for elem in buf.iter().take(buf.len() - 1) { assert_eq!(*elem.num_clones.borrow(), 0); } } #[test] fn fill_unwind_safety() { // This needs to be `static` to be used in `fn clone()` below static mut TRACKER: Option> = None; // SAFETY: the assumption is that this test function will be called only once unsafe { TRACKER.replace(DropTracker::new()); } fn tracker() -> &'static DropTracker { unsafe { TRACKER.as_ref().unwrap() } } fn tracker_mut() -> &'static mut DropTracker { unsafe { TRACKER.as_mut().unwrap() } } #[derive(Debug)] struct FaultyClonable { drop_item: DropItem, num_clones: RefCell, panic_at: usize, } impl Clone for FaultyClonable { fn clone(&self) -> Self { *self.num_clones.borrow_mut() += 1; let num_clones = *self.num_clones.borrow(); if num_clones >= self.panic_at { panic!("clone failed :("); } Self { drop_item: tracker_mut() .track(format!("clone #{} of {}", num_clones, self.drop_item)), num_clones: RefCell::default(), panic_at: self.panic_at, } } } let mut buf = CircularBuffer::<6, FaultyClonable>::new(); let value = FaultyClonable { drop_item: tracker_mut().track("value".to_string()), num_clones: RefCell::default(), panic_at: 4, }; let res = std::panic::catch_unwind(move || buf.fill(value)); assert!(res.is_err()); tracker().assert_dropped("value"); tracker().assert_dropped("clone #1 of value"); tracker().assert_dropped("clone #2 of value"); tracker().assert_dropped("clone #3 of value"); assert!(!tracker().is_tracked("clone #4 of value")); assert!(!tracker().is_tracked("clone #5 of value")); } #[test] fn fill_with_unwind_safety() { // This needs to be `static` to be used in `fn closure()` below static mut TRACKER: Option> = None; // SAFETY: the assumption is that this test function will be called only once unsafe { TRACKER.replace(DropTracker::new()); } fn tracker() -> &'static DropTracker { unsafe { TRACKER.as_ref().unwrap() } } fn tracker_mut() -> &'static mut DropTracker { unsafe { TRACKER.as_mut().unwrap() } } let mut buf = CircularBuffer::<6, DropItem>::new(); let res = std::panic::catch_unwind(move || { let mut counter = 0u32; buf.fill_with(|| { counter += 1; if counter > 4 { panic!("closure failed :("); } tracker_mut().track(counter) }) }); assert!(res.is_err()); tracker().assert_dropped(&1); tracker().assert_dropped(&2); tracker().assert_dropped(&3); tracker().assert_dropped(&4); assert!(!tracker().is_tracked(&5)); assert!(!tracker().is_tracked(&6)); } #[test] fn make_contiguous_full() { let mut buf: CircularBuffer<4, u32> = [1, 2, 3, 4].into_iter().collect(); assert_buf_slices_eq!(buf, [1, 2, 3, 4], []); assert_eq!(buf.make_contiguous(), &mut [1, 2, 3, 4]); assert_buf_slices_eq!(buf, [1, 2, 3, 4], []); assert_buf_eq!(buf, [1, 2, 3, 4]); buf.push_back(5); assert_buf_slices_eq!(buf, [2, 3, 4], [5]); assert_eq!(buf.make_contiguous(), &mut [2, 3, 4, 5]); assert_buf_slices_eq!(buf, [2, 3, 4, 5], []); assert_buf_eq!(buf, [2, 3, 4, 5]); buf.extend([6, 7]); assert_buf_slices_eq!(buf, [4, 5], [6, 7]); assert_eq!(buf.make_contiguous(), &mut [4, 5, 6, 7]); assert_buf_slices_eq!(buf, [4, 5, 6, 7], []); assert_buf_eq!(buf, [4, 5, 6, 7]); buf.extend([8, 9, 10]); assert_buf_slices_eq!(buf, [7], [8, 9, 10]); assert_eq!(buf.make_contiguous(), &mut [7, 8, 9, 10]); assert_buf_slices_eq!(buf, [7, 8, 9, 10], []); assert_buf_eq!(buf, [7, 8, 9, 10]); } #[test] fn make_contiguous_not_full() { let mut buf: CircularBuffer<4, u32> = [1, 2].into_iter().collect(); assert_buf_slices_eq!(buf, [1, 2], []); assert_eq!(buf.make_contiguous(), &mut [1, 2]); assert_buf_slices_eq!(buf, [1, 2], []); assert_buf_eq!(buf, [1, 2]); buf.extend([3, 4, 5]); buf.truncate_front(2); assert_buf_slices_eq!(buf, [4], [5]); assert_eq!(buf.make_contiguous(), &mut [4, 5]); assert_buf_slices_eq!(buf, [4, 5], []); assert_buf_eq!(buf, [4, 5]); } #[test] fn clone() { let mut buf = CircularBuffer::<4, u32>::new(); assert_eq!(buf, buf.clone()); buf.extend_from_slice(&[][..]); assert_eq!(buf, buf.clone()); buf.extend_from_slice(&[1][..]); assert_eq!(buf, buf.clone()); buf.extend_from_slice(&[2, 3][..]); assert_eq!(buf, buf.clone()); buf.extend_from_slice(&[4, 5, 6][..]); assert_eq!(buf, buf.clone()); buf.extend_from_slice(&[7, 8, 9, 10][..]); assert_eq!(buf, buf.clone()); buf.extend_from_slice(&[11, 12, 13, 14, 15][..]); assert_eq!(buf, buf.clone()); } #[test] fn hash() { fn hash(buf: &CircularBuffer) -> u64 { let mut hasher = DefaultHasher::new(); buf.hash(&mut hasher); hasher.finish() } let hash_empty = hash(&CircularBuffer::<0, u32>::new()); assert_eq!(hash_empty, hash(&CircularBuffer::<0, u32>::new())); assert_eq!(hash_empty, hash(&CircularBuffer::<2, u32>::new())); assert_eq!(hash_empty, hash(&CircularBuffer::<4, u32>::new())); assert_eq!(hash_empty, hash(&CircularBuffer::<8, u32>::new())); let hash_1 = hash(&CircularBuffer::<1, u32>::from([1])); assert_ne!(hash_1, hash_empty); assert_eq!(hash_1, hash(&CircularBuffer::<2, u32>::from([1]))); assert_eq!(hash_1, hash(&CircularBuffer::<4, u32>::from([1]))); assert_eq!(hash_1, hash(&CircularBuffer::<8, u32>::from([1]))); let hash_2 = hash(&CircularBuffer::<2, u32>::from([1, 2])); assert_ne!(hash_2, hash_empty); assert_ne!(hash_2, hash_1); assert_eq!(hash_2, hash(&CircularBuffer::<4, u32>::from([1, 2]))); assert_eq!(hash_2, hash(&CircularBuffer::<8, u32>::from([1, 2]))); let hash_4 = hash(&CircularBuffer::<4, u32>::from([1, 2, 3, 4])); assert_ne!(hash_4, hash_empty); assert_ne!(hash_4, hash_1); assert_ne!(hash_4, hash_2); assert_eq!(hash_4, hash(&CircularBuffer::<4, u32>::from([1, 2, 3, 4]))); assert_eq!(hash_4, hash(&CircularBuffer::<8, u32>::from([1, 2, 3, 4]))); } #[test] fn debug() { let mut buf = CircularBuffer::<4, u32>::new(); assert_buf_eq!(buf, [] as [u32; 0]); assert_eq!(format!("{:?}", buf), "[]"); assert_eq!(format!("{:x?}", buf), "[]"); assert_eq!(format!("{:X?}", buf), "[]"); buf.push_back(10); assert_buf_eq!(buf, [10]); assert_eq!(format!("{:?}", buf), "[10]"); assert_eq!(format!("{:x?}", buf), "[a]"); assert_eq!(format!("{:X?}", buf), "[A]"); buf.push_back(20); assert_buf_eq!(buf, [10, 20]); assert_eq!(format!("{:?}", buf), "[10, 20]"); assert_eq!(format!("{:x?}", buf), "[a, 14]"); assert_eq!(format!("{:X?}", buf), "[A, 14]"); buf.push_back(30); assert_buf_eq!(buf, [10, 20, 30]); assert_eq!(format!("{:?}", buf), "[10, 20, 30]"); assert_eq!(format!("{:x?}", buf), "[a, 14, 1e]"); assert_eq!(format!("{:X?}", buf), "[A, 14, 1E]"); buf.push_back(40); assert_buf_eq!(buf, [10, 20, 30, 40]); assert_eq!(format!("{:?}", buf), "[10, 20, 30, 40]"); assert_eq!(format!("{:x?}", buf), "[a, 14, 1e, 28]"); assert_eq!(format!("{:X?}", buf), "[A, 14, 1E, 28]"); buf.push_back(50); assert_buf_eq!(buf, [20, 30, 40, 50]); assert_eq!(format!("{:?}", buf), "[20, 30, 40, 50]"); assert_eq!(format!("{:x?}", buf), "[14, 1e, 28, 32]"); assert_eq!(format!("{:X?}", buf), "[14, 1E, 28, 32]"); buf.push_back(60); assert_buf_eq!(buf, [30, 40, 50, 60]); assert_eq!(format!("{:?}", buf), "[30, 40, 50, 60]"); assert_eq!(format!("{:x?}", buf), "[1e, 28, 32, 3c]"); assert_eq!(format!("{:X?}", buf), "[1E, 28, 32, 3C]"); } macro_rules! assert_add_mod_eq { ( $expected:expr , crate :: add_mod ( $x:expr , $y:expr , $m:expr ) ) => { let x = $x; let y = $y; let m = $m; let expected = $expected; let result = crate::add_mod(x, y, m); assert_eq!( result, expected, "add_mod({x}, {y}, {m}) returned {result}; expected: {expected}" ); }; } #[test] fn add_mod() { assert_eq!(0, crate::add_mod(0, 0, 1)); assert_eq!(0, crate::add_mod(0, 1, 1)); assert_eq!(0, crate::add_mod(1, 0, 1)); assert_eq!(0, crate::add_mod(1, 1, 1)); assert_eq!(0, crate::add_mod(0, 0, 2)); assert_eq!(1, crate::add_mod(0, 1, 2)); assert_eq!(0, crate::add_mod(0, 2, 2)); assert_eq!(1, crate::add_mod(1, 0, 2)); assert_eq!(0, crate::add_mod(1, 1, 2)); assert_eq!(1, crate::add_mod(1, 2, 2)); assert_eq!(0, crate::add_mod(2, 0, 2)); assert_eq!(1, crate::add_mod(2, 1, 2)); assert_eq!(0, crate::add_mod(2, 2, 2)); for m in [ 3, 4, 5, 6, 7, 8, usize::MAX >> 1, (usize::MAX >> 1) + 1, usize::MAX - 2, usize::MAX - 1, usize::MAX, ] { assert_add_mod_eq!(0, crate::add_mod(0, 0, m)); assert_add_mod_eq!(0, crate::add_mod(0, m, m)); assert_add_mod_eq!(0, crate::add_mod(m, 0, m)); assert_add_mod_eq!(0, crate::add_mod(m, m, m)); assert_add_mod_eq!(1, crate::add_mod(1, m, m)); assert_add_mod_eq!(2, crate::add_mod(2, m, m)); assert_add_mod_eq!(m - 2, crate::add_mod(m - 2, m, m)); assert_add_mod_eq!(m - 1, crate::add_mod(m - 1, m, m)); } } circular-buffer-1.0.0/tests/covariance.rs000064400000000000000000000026261046102023000165250ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause #![allow(clippy::extra_unused_lifetimes)] /// Tests to verify that certain types offered by the crate are [covariant]. /// /// [covariant]: https://doc.rust-lang.org/nomicon/subtyping.html use circular_buffer::CircularBuffer; use circular_buffer::Drain; use circular_buffer::Iter; /// Verify that `CircularBuffer` is covariant over `T` #[test] fn circular_buffer<'a>() { let buf = CircularBuffer::<1, &'static str>::new(); let _: CircularBuffer<1, &'a str> = buf; } /// Verify that `Iter<'_, T>` is covariant over `T` #[test] fn iter<'a>() { let buf = CircularBuffer::<1, &'static str>::new(); let iter: Iter<'_, &'static str> = buf.iter(); let _: Iter<'_, &'a str> = iter; } // `IterMut<'a, T>` is invariant over `T` because it holds a mutable reference to the elements of // the buffer. // // TODO Add a test to verify the invariance of `IterMut` // //#[test] //fn iter_mut<'a>() { // let mut buf = CircularBuffer::<1, &'static str>::new(); // let iter: IterMut<'_, &'static str> = buf.iter_mut(); // let _: IterMut<'_, &'a str> = iter; //} /// Verify that `Drain<'_, N, T>` is covariant over `T` #[test] fn drain<'a>() { let mut buf = CircularBuffer::<1, &'static str>::new(); let drain: Drain<'_, 1, &'static str> = buf.drain(..); let _: Drain<'_, 1, &'a str> = drain; } circular-buffer-1.0.0/tests/io.rs000064400000000000000000000164441046102023000150250ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause #[cfg(feature = "std")] mod std { use circular_buffer::CircularBuffer; use std::io::BufRead; use std::io::Read; use std::io::Write; #[test] fn write() { let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert!(write!(&mut buf, "hello").is_ok()); assert_eq!(buf, [b'e', b'l', b'l', b'o']); assert!(write!(&mut buf, "world").is_ok()); assert_eq!(buf, [b'o', b'r', b'l', b'd']); } #[test] fn read() { fn read_all(mut buf: R) -> Vec { let mut vec = Vec::new(); buf.read_to_end(&mut vec).expect("read failed"); vec } let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert_eq!(read_all(&mut buf), []); assert_eq!(buf, [] as [u8; 0]); buf.push_back(b'a'); buf.push_back(b'b'); assert_eq!(buf, [b'a', b'b']); assert_eq!(read_all(&mut buf), [b'a', b'b']); assert_eq!(buf, [] as [u8; 0]); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(read_all(&mut buf), [b'c', b'd', b'e', b'f']); assert_eq!(buf, [] as [u8; 0]); } #[test] fn read_buf() { let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert_eq!(buf.fill_buf().unwrap(), b""); buf.push_back(b'a'); buf.push_back(b'b'); assert_eq!(buf, [b'a', b'b']); assert_eq!(buf.fill_buf().unwrap(), b"ab"); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(buf.fill_buf().unwrap(), b"cd"); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b"ef"); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b""); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b""); } } #[cfg(feature = "embedded-io")] mod embedded_io { use circular_buffer::CircularBuffer; use embedded_io::BufRead; use embedded_io::Read; use embedded_io::Write; #[test] fn write() { let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert!(write!(&mut buf, "hello").is_ok()); assert_eq!(buf, [b'e', b'l', b'l', b'o']); assert!(write!(&mut buf, "world").is_ok()); assert_eq!(buf, [b'o', b'r', b'l', b'd']); } #[test] fn read() { fn read_all(mut buf: R) -> Vec { let mut vec = Vec::new(); loop { let mut tmp = [0u8; 4]; let size = buf.read(&mut tmp[..]).expect("read failed"); if size == 0 { break; } vec.extend_from_slice(&tmp[..size]); } vec } let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert_eq!(read_all(&mut buf), []); assert_eq!(buf, [] as [u8; 0]); buf.push_back(b'a'); buf.push_back(b'b'); assert_eq!(buf, [b'a', b'b']); assert_eq!(read_all(&mut buf), [b'a', b'b']); assert_eq!(buf, [] as [u8; 0]); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(read_all(&mut buf), [b'c', b'd', b'e', b'f']); assert_eq!(buf, [] as [u8; 0]); } #[test] fn read_buf() { let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert_eq!(buf.fill_buf().unwrap(), b""); buf.push_back(b'a'); buf.push_back(b'b'); assert_eq!(buf, [b'a', b'b']); assert_eq!(buf.fill_buf().unwrap(), b"ab"); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(buf.fill_buf().unwrap(), b"cd"); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b"ef"); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b""); buf.consume(2); assert_eq!(buf.fill_buf().unwrap(), b""); } } #[cfg(feature = "embedded-io-async")] mod embedded_io_async { use circular_buffer::CircularBuffer; use embedded_io_async::BufRead; use embedded_io_async::Read; use embedded_io_async::Write; #[test] fn write() { futures_lite::future::block_on(async { let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert!(buf.write_all(b"hello").await.is_ok()); assert_eq!(buf, [b'e', b'l', b'l', b'o']); assert!(buf.write_all(b"world").await.is_ok()); assert_eq!(buf, [b'o', b'r', b'l', b'd']); }); } #[test] fn read() { futures_lite::future::block_on(async { async fn read_all(mut buf: R) -> Vec { let mut vec = Vec::new(); loop { let mut tmp = [0u8; 4]; let size = buf.read(&mut tmp[..]).await.expect("read failed"); if size == 0 { break; } vec.extend_from_slice(&tmp[..size]); } vec } let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert_eq!(read_all(&mut buf).await, []); assert_eq!(buf, [] as [u8; 0]); buf.push_back(b'a'); buf.push_back(b'b'); assert_eq!(buf, [b'a', b'b']); assert_eq!(read_all(&mut buf).await, [b'a', b'b']); assert_eq!(buf, [] as [u8; 0]); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(read_all(&mut buf).await, [b'c', b'd', b'e', b'f']); assert_eq!(buf, [] as [u8; 0]); }); } #[test] fn read_buf() { futures_lite::future::block_on(async { let mut buf = CircularBuffer::<4, u8>::new(); assert_eq!(buf, [] as [u8; 0]); assert_eq!(buf.fill_buf().await.unwrap(), b""); buf.push_back(b'a'); buf.push_back(b'b'); assert_eq!(buf, [b'a', b'b']); assert_eq!(buf.fill_buf().await.unwrap(), b"ab"); buf.push_back(b'c'); buf.push_back(b'd'); buf.push_back(b'e'); buf.push_back(b'f'); assert_eq!(buf, [b'c', b'd', b'e', b'f']); assert_eq!(buf.fill_buf().await.unwrap(), b"cd"); buf.consume(2); assert_eq!(buf.fill_buf().await.unwrap(), b"ef"); buf.consume(2); assert_eq!(buf.fill_buf().await.unwrap(), b""); buf.consume(2); assert_eq!(buf.fill_buf().await.unwrap(), b""); }); } } circular-buffer-1.0.0/tests/large.rs000064400000000000000000000025311046102023000155000ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause #![cfg(feature = "std")] use circular_buffer::CircularBuffer; #[cfg(not(miri))] const SIZE: usize = 2 * 1024 * 1024; // 2 MiB #[cfg(miri)] const SIZE: usize = 2 * 1024; // 2 KiB #[test] fn large_boxed() { let chunk = b"abcdefghijklmnopqrstuvxyz0123456789"; let mut buf = CircularBuffer::::boxed(); let mut vec = Vec::new(); assert_ne!(SIZE % chunk.len(), 0); assert_eq!(buf.len(), 0); assert!(buf.is_empty()); assert!(!buf.is_full()); assert_eq!(buf.as_slices().0, &[][..]); assert_eq!(buf.as_slices().1, &[][..]); for _ in 0..(SIZE / chunk.len()) { buf.extend_from_slice(&chunk[..]); vec.extend_from_slice(&chunk[..]); assert_eq!(buf.len(), vec.len()); assert!(!buf.is_empty()); assert!(!buf.is_full()); assert_eq!(buf.as_slices().0, &vec[..]); assert_eq!(buf.as_slices().1, &[][..]); } for _ in 0..(SIZE / chunk.len()) { buf.extend_from_slice(&chunk[..]); vec.extend_from_slice(&chunk[..]); assert_eq!(buf.len(), SIZE); assert!(!buf.is_empty()); assert!(buf.is_full()); assert_eq!(buf.as_slices().0, &vec[vec.len() - SIZE..SIZE]); assert_eq!(buf.as_slices().1, &vec[SIZE..]); } } circular-buffer-1.0.0/tests/randomized.rs000064400000000000000000000341651046102023000165520ustar 00000000000000// Copyright © 2023-2025 Andrea Corbellini and contributors // SPDX-License-Identifier: BSD-3-Clause #![allow(static_mut_refs)] #![cfg(feature = "std")] //! Compare the correctness of `CircularBuffer` against a reference implementation (that is assumed //! to be fully correct). //! //! This module applies random actions (like `push_back`, `pop_front`, ...) to a `CircularBuffer` //! and to a reference implementation at the same time, and compares their result after each //! action. The reference implementation currently is based on top of `VecDeque`. use circular_buffer::CircularBuffer; use drop_tracker::DropItem; use drop_tracker::DropTracker; use rand::distributions::Distribution; use rand::distributions::Standard; use rand::distributions::Uniform; use rand::Rng; use std::collections::VecDeque; use std::fmt; use std::mem; use std::ops::Deref; use std::ops::DerefMut; use std::ops::RangeInclusive; use std::rc::Rc; #[cfg(not(miri))] const ROUNDS: usize = 200_000; #[cfg(miri)] const ROUNDS: usize = 200; #[derive(Clone, Debug)] enum Action { BackMut(T), FrontMut(T), GetMut(usize, T), PushBack(T), PushFront(T), PopBack, PopFront, Remove(usize), Swap(usize, usize), SwapRemoveBack(usize), SwapRemoveFront(usize), TruncateBack(usize), TruncateFront(usize), Clear, Extend(Vec), ExtendFromSlice(Vec), RangeMut(RangeInclusive, Vec), Drain(RangeInclusive), MakeContiguous, } impl Distribution> for Standard where Standard: Distribution, { fn sample(&self, rng: &mut R) -> Action { fn random_vec(rng: &mut R) -> Vec where Standard: Distribution, { let size = rng.gen_range(0..128); let mut vec = Vec::with_capacity(size); for _ in 0..size { vec.push(rng.gen()); } vec } let action_num: u8 = rng.gen_range(0..=6); match action_num { 0 => Action::PushBack(rng.gen()), 1 => Action::PushFront(rng.gen()), 2 => Action::PopBack, 3 => Action::PopFront, 4 => Action::Clear, 5 => Action::Extend(random_vec(rng)), 6 => Action::ExtendFromSlice(random_vec(rng)), _ => unreachable!(), } } } impl Distribution> for Uniform where Standard: Distribution, { fn sample(&self, rng: &mut R) -> Action { fn random_vec(rng: &mut R) -> Vec where Standard: Distribution, { let size = rng.gen_range(0..128); let mut vec = Vec::with_capacity(size); for _ in 0..size { vec.push(rng.gen()); } vec } fn random_range, R: Rng + ?Sized>( dist: &D, rng: &mut R, ) -> RangeInclusive { let low = dist.sample(rng); let high = dist.sample(rng).max(low); low..=high } let action_num: u8 = rng.gen_range(0..=18); match action_num { 0 => Action::BackMut(rng.gen()), 1 => Action::FrontMut(rng.gen()), 2 => Action::GetMut(self.sample(rng), rng.gen()), 3 => Action::PushBack(rng.gen()), 4 => Action::PushFront(rng.gen()), 5 => Action::PopBack, 6 => Action::PopFront, 7 => Action::Remove(self.sample(rng)), 8 => Action::Swap(self.sample(rng), self.sample(rng)), 9 => Action::SwapRemoveBack(self.sample(rng)), 10 => Action::SwapRemoveFront(self.sample(rng)), 11 => Action::TruncateBack(self.sample(rng)), 12 => Action::TruncateFront(self.sample(rng)), 13 => Action::Clear, 14 => Action::Extend(random_vec(rng)), 15 => Action::ExtendFromSlice(random_vec(rng)), 16 => Action::RangeMut(random_range(self, rng), random_vec(rng)), 17 => Action::Drain(random_range(self, rng)), 18 => Action::MakeContiguous, _ => unreachable!(), } } } #[derive(Copy, Clone, Debug)] enum Direction { Back, Front, } #[derive(Debug)] struct Reference { inner: VecDeque, max_len: usize, } impl Reference { fn new(max_len: usize) -> Self { Self { inner: VecDeque::new(), max_len, } } fn trim(&mut self, direction: Direction) { match direction { Direction::Back => self.trim_back(), Direction::Front => self.trim_front(), } } fn trim_back(&mut self) { while self.len() > self.max_len { self.pop_back().unwrap(); } } fn trim_front(&mut self) { while self.len() > self.max_len { self.pop_front().unwrap(); } } } impl Deref for Reference { type Target = VecDeque; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for Reference { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } #[derive(Clone, PartialEq, Eq, Debug)] enum Result { None, Val(T), Vec(Vec), } impl From> for Result { fn from(opt: Option) -> Self { match opt { Some(val) => Self::Val(val), None => Self::None, } } } impl FromIterator for Result { fn from_iter(iter: I) -> Self where I: IntoIterator, { let vec = Vec::from_iter(iter); Self::Vec(vec) } } trait Perform { fn perform(&mut self, action: Action) -> Result; } impl Perform for CircularBuffer where T: Clone, { fn perform(&mut self, action: Action) -> Result { match action { Action::BackMut(elem) => { *self.back_mut().unwrap() = elem; Result::None } Action::FrontMut(elem) => { *self.front_mut().unwrap() = elem; Result::None } Action::GetMut(index, elem) => { *self.get_mut(index).unwrap() = elem; Result::None } Action::PushBack(elem) => { self.push_back(elem); Result::None } Action::PushFront(elem) => { self.push_front(elem); Result::None } Action::PopBack => self.pop_back().into(), Action::PopFront => self.pop_front().into(), Action::Remove(index) => self.remove(index).into(), Action::Swap(x, y) => { self.swap(x, y); Result::None } Action::SwapRemoveBack(index) => { self.swap_remove_back(index); Result::None } Action::SwapRemoveFront(index) => { self.swap_remove_front(index); Result::None } Action::TruncateBack(index) => { self.truncate_back(index); Result::None } Action::TruncateFront(index) => { self.truncate_front(index); Result::None } Action::Clear => { self.clear(); Result::None } Action::Extend(elems) => { self.extend(elems); Result::None } Action::ExtendFromSlice(elems) => { self.extend_from_slice(&elems[..]); Result::None } Action::RangeMut(range, elems) => { self.range_mut(range) .zip(elems) .map(|(elem, replacement)| *elem = replacement) .count(); Result::None } Action::Drain(range) => self.drain(range).collect(), Action::MakeContiguous => self.make_contiguous().iter().cloned().collect(), } } } impl Perform for VecDeque where T: Clone, { fn perform(&mut self, action: Action) -> Result { match action { Action::BackMut(elem) => { *self.back_mut().unwrap() = elem; Result::None } Action::FrontMut(elem) => { *self.front_mut().unwrap() = elem; Result::None } Action::GetMut(index, elem) => { *self.get_mut(index).unwrap() = elem; Result::None } Action::PushBack(elem) => { self.push_back(elem); Result::None } Action::PushFront(elem) => { self.push_front(elem); Result::None } Action::PopBack => self.pop_back().into(), Action::PopFront => self.pop_front().into(), Action::Remove(index) => self.remove(index).into(), Action::Swap(x, y) => { self.swap(x, y); Result::None } Action::SwapRemoveBack(index) => { self.swap_remove_back(index); Result::None } Action::SwapRemoveFront(index) => { self.swap_remove_front(index); Result::None } Action::TruncateBack(size) => { while self.len() > size { let _ = self.pop_back(); } Result::None } Action::TruncateFront(size) => { while self.len() > size { let _ = self.pop_front(); } Result::None } Action::Clear => { self.clear(); Result::None } Action::Extend(elems) => { self.extend(elems); Result::None } Action::ExtendFromSlice(elems) => { self.extend(elems); Result::None } Action::RangeMut(range, elems) => { self.range_mut(range) .zip(elems) .map(|(elem, replacement)| *elem = replacement) .count(); Result::None } Action::Drain(range) => self.drain(range).collect(), Action::MakeContiguous => self.make_contiguous().iter().cloned().collect(), } } } impl Perform for Reference where T: Clone, { fn perform(&mut self, action: Action) -> Result { let trim_direction = match action { Action::PushBack(_) => Some(Direction::Front), Action::PushFront(_) => Some(Direction::Back), Action::Extend(_) => Some(Direction::Front), Action::ExtendFromSlice(_) => Some(Direction::Front), _ => None, }; let result = self.inner.perform(action); if let Some(direction) = trim_direction { self.trim(direction); } result } } fn test() where T: Clone + PartialEq + fmt::Debug, Standard: Distribution, { let mut reference = Reference::::new(N); let mut buffer = CircularBuffer::::boxed(); let mut rng = rand::thread_rng(); for _ in 0..ROUNDS { // Generate a random action let action: Action = if reference.is_empty() { >>::sample(&Standard, &mut rng) } else { Uniform::from(0..reference.len()).sample(&mut rng) }; println!("{action:?}"); // Perform the action on both the reference implementation and the CircularBuffer let expected = reference.perform(action.clone()); let actual = buffer.perform(action); // Compare the return value of both implementations assert_eq!(expected, actual); // Compare the state of both implementations let expected_items = reference.iter().cloned().collect::>(); #[allow(clippy::eq_op)] { assert_eq!(buffer, buffer); } assert_eq!(*buffer, &expected_items[..]); assert_eq!(buffer.to_vec(), expected_items); assert_eq!(reference.len(), buffer.len()); assert_eq!(reference.is_empty(), buffer.is_empty()); assert_eq!( reference.iter().collect::>(), buffer.iter().collect::>() ); assert_eq!( reference.iter_mut().collect::>(), buffer.iter_mut().collect::>() ); assert_eq!( reference.iter().rev().collect::>(), buffer.iter().rev().collect::>() ); assert_eq!( reference.iter_mut().rev().collect::>(), buffer.iter_mut().rev().collect::>() ); } } #[test] fn zero() { test::<0, u64>(); } #[test] fn small() { test::<10, u64>(); } #[test] fn medium() { test::<1_000, u64>(); } #[test] fn large() { test::<1_000_000, u64>(); } #[test] fn largest_with_zero_sized_struct() { type Zst = (); assert_eq!(mem::size_of::(), 0); test::<{ usize::MAX }, Zst>(); } #[test] fn drop() { static mut TRACKER: Option> = None; // SAFETY: the assumption is that this test function will be called only once unsafe { TRACKER.replace(DropTracker::new()); } fn tracker() -> &'static DropTracker { unsafe { TRACKER.as_ref().unwrap() } } fn tracker_mut() -> &'static mut DropTracker { unsafe { TRACKER.as_mut().unwrap() } } #[derive(Clone, PartialEq, Eq, Debug)] struct Item(Rc>); impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Item { let n = rng.gen(); Item(Rc::new(tracker_mut().track(n))) } } test::<100, Item>(); tracker().assert_fully_dropped(); }