diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 783bbc3..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: rustlang/rust:nightly - steps: - - checkout - - run: - name: Build - command: | - cargo build - cargo build --release - - run: - name: Test - command: cargo test - - run: - name: Benchmark - command: cargo bench diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..1557067 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + - package-ecosystem: cargo + directory: / + schedule: + interval: daily diff --git a/.github/workflows/bench.yaml b/.github/workflows/bench.yaml new file mode 100644 index 0000000..bb911fc --- /dev/null +++ b/.github/workflows/bench.yaml @@ -0,0 +1,35 @@ +name: bench +on: + push: + branches: + - main + pull_request: +concurrency: + group: bench-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} +jobs: + cargo: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: swatinem/rust-cache@v2 + - run: cargo bench + codspeed: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: swatinem/rust-cache@v2 + - run: cargo install --features vendored-openssl cargo-codspeed + - run: cargo codspeed build + - uses: codspeedhq/action@v3 + with: + run: cargo codspeed run + token: ${{ secrets.CODSPEED_TOKEN }} + bench: + needs: + - cargo + - codspeed + if: always() + runs-on: ubuntu-latest + steps: + - run: for result in ${{ join(needs.*.result, ' ') }}; do [ $result = success ]; done diff --git a/.github/workflows/dependabot.yaml b/.github/workflows/dependabot.yaml new file mode 100644 index 0000000..b4e087b --- /dev/null +++ b/.github/workflows/dependabot.yaml @@ -0,0 +1,13 @@ +name: dependabot +on: pull_request +permissions: + contents: write + pull-requests: write +jobs: + merge: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - run: gh pr merge --auto --squash ${{ github.event.pull_request.html_url }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..82ae86e --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,30 @@ +name: lint +on: + push: + branches: + - main + pull_request: +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: swatinem/rust-cache@v2 + - run: cargo clippy -- -D warnings + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - run: cargo fmt -- --check + spell_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: streetsidesoftware/cspell-action@v7 + with: + files: "**/*.{md,rs}" + readme: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: raviqqe/markdown-link-check@v1 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..30c1485 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,23 @@ +name: release +on: + push: + branches: + - main + pull_request: +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} +jobs: + release: + runs-on: ubuntu-latest + environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }} + steps: + - uses: actions/checkout@v5 + with: + submodules: true + - uses: raviqqe/cargo-cache@v1 + - run: cargo install cargo-workspaces + - run: cargo workspaces publish -y --from-git + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..01fec1b --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,19 @@ +name: test +on: + push: + branches: + - main + pull_request: +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: swatinem/rust-cache@v2 + - run: cargo build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: swatinem/rust-cache@v2 + - run: cargo test diff --git a/Cargo.toml b/Cargo.toml index 373573e..ee00b56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,41 @@ [package] name = "array-queue" -description = "Fixed size bidirectional queues based on arrays" version = "0.3.3" authors = ["Yota Toyama "] +edition = "2024" +description = "Fixed size bidirectional queues based on arrays" license = "MIT" [dependencies] arrayvec = "0.4" + +[lints.clippy] +alloc_instead_of_core = "deny" +complexity = "deny" +correctness = "deny" +dbg_macro = "deny" +derive_partial_eq_without_eq = "deny" +equatable_if_let = "deny" +explicit_deref_methods = "deny" +if_not_else = "deny" +manual_let_else = "deny" +missing_const_for_fn = "deny" +missing_panics_doc = "deny" +multiple_crate_versions = { level = "allow", priority = 1 } +option_if_let_else = "deny" +perf = "deny" +std_instead_of_alloc = "deny" +std_instead_of_core = "deny" +style = "deny" +suspicious = "deny" +todo = "deny" +undocumented_unsafe_blocks = "deny" +unimplemented = "deny" +uninlined_format_args = "deny" +unnecessary_safety_comment = "deny" +unused_self = "deny" +use_self = "deny" + +[lints.rust] +missing_docs = "deny" +warnings = "deny" diff --git a/cspell.json b/cspell.json new file mode 100644 index 0000000..1882f4f --- /dev/null +++ b/cspell.json @@ -0,0 +1,3 @@ +{ + "words": ["arrayvec", "clippy", "uninit", "uninlined", "yota"] +} diff --git a/src/array_queue.rs b/src/array_queue.rs index 35c16ac..4aadc8b 100644 --- a/src/array_queue.rs +++ b/src/array_queue.rs @@ -1,331 +1,307 @@ -use std::mem::{drop, forget, replace, uninitialized, ManuallyDrop}; - -use arrayvec::Array; - -use super::error::CapacityError; +use crate::error::CapacityError; +use core::mem::{MaybeUninit, replace, transmute}; +/// A queue backed by a fixed-size array. #[derive(Debug)] -pub struct ArrayQueue::Item]> + AsMut<[::Item]>> { - array: ManuallyDrop, +pub struct ArrayQueue { + array: [MaybeUninit; N], start: usize, length: usize, } -impl::Item]> + AsMut<[::Item]>> ArrayQueue { - pub fn new() -> Self { - ArrayQueue { - array: unsafe { uninitialized() }, +impl ArrayQueue { + /// Creates an empty queue. + pub const fn new() -> Self { + Self { + array: [const { MaybeUninit::uninit() }; N], start: 0, length: 0, } } - pub fn first(&self) -> Option<&::Item> { + /// Returns a reference to the first element of the queue, or `None` if it is empty. + pub const fn first(&self) -> Option<&T> { self.element(0) } - pub fn first_mut(&mut self) -> Option<&mut ::Item> { + /// Returns a mutable reference to the first element of the queue, or `None` if it is empty. + pub const fn first_mut(&mut self) -> Option<&mut T> { self.element_mut(0) } - pub fn last(&self) -> Option<&::Item> { + /// Returns a reference to the last element of the queue, or `None` if it is empty. + pub const fn last(&self) -> Option<&T> { if self.is_empty() { - return None; + None + } else { + self.element(self.length - 1) } - - self.element(self.length - 1) } - pub fn last_mut(&mut self) -> Option<&mut ::Item> { + /// Returns a mutable reference to the last element of the queue, or `None` if it is empty. + pub const fn last_mut(&mut self) -> Option<&mut T> { if self.is_empty() { - return None; + None + } else { + self.element_mut(self.length - 1) } - - let i = self.length - 1; - self.element_mut(i) } - fn element(&self, i: usize) -> Option<&::Item> { - if self.is_empty() { - None + const fn element(&self, index: usize) -> Option<&T> { + if index < self.length { + let x = &self.array[self.index(index)]; + + // SAFETY: We validate the element existence by the length check. + Some(unsafe { x.assume_init_ref() }) } else { - Some(&self.array.as_ref()[self.index(i)]) + None } } - fn element_mut(&mut self, i: usize) -> Option<&mut ::Item> { - if self.is_empty() { - None + const fn element_mut(&mut self, index: usize) -> Option<&mut T> { + if index < self.length { + let x = &mut self.array[self.index(index)]; + + // SAFETY: We validate the element existence by the length check. + Some(unsafe { x.assume_init_mut() }) } else { - let i = self.index(i); - Some(&mut self.array.as_mut()[i]) + None } } - pub fn push_back(&mut self, x: &::Item) -> Result<(), CapacityError> - where - ::Item: Clone, - { + /// Pushes an element to the front of the queue. + pub fn push_front(&mut self, x: T) -> Result<(), CapacityError> { if self.is_full() { return Err(CapacityError); } - let i = self.index(self.length); - forget(replace(&mut self.array.as_mut()[i], x.clone())); + self.start = self.index(N - 1); + self.array[self.start] = MaybeUninit::new(x); self.length += 1; + Ok(()) } - pub fn push_front(&mut self, x: &::Item) -> Result<(), CapacityError> - where - ::Item: Clone, - { + /// Pushes an element to the back of the queue. + pub fn push_back(&mut self, x: T) -> Result<(), CapacityError> { if self.is_full() { return Err(CapacityError); } - self.start = self.index(Self::capacity() - 1); - forget(replace(&mut self.array.as_mut()[self.start], x.clone())); + self.array[self.index(self.length)] = MaybeUninit::new(x); self.length += 1; + Ok(()) } - pub fn pop_back(&mut self) -> Option<::Item> { + /// Pops an element from the front of the queue. + pub const fn pop_front(&mut self) -> Option { if self.is_empty() { - return None; - } + None + } else { + let x = replace(&mut self.array[self.start], MaybeUninit::uninit()); + self.start = self.index(1); + self.length -= 1; - let x = replace(&mut self.array.as_mut()[self.length - 1], unsafe { - uninitialized() - }); - self.length -= 1; - Some(x) + // SAFETY: An element exists at the first index. + Some(unsafe { x.assume_init() }) + } } - pub fn pop_front(&mut self) -> Option<::Item> { + /// Pops an element from the back of the queue. + pub const fn pop_back(&mut self) -> Option { if self.is_empty() { - return None; - } + None + } else { + let x = replace(&mut self.array[self.length - 1], MaybeUninit::uninit()); + self.length -= 1; - let x = replace(&mut self.array.as_mut()[self.start], unsafe { - uninitialized() - }); - self.start = self.index(1); - self.length -= 1; - Some(x) + // SAFETY: An element exists at the last index. + Some(unsafe { x.assume_init() }) + } } - pub fn len(&self) -> usize { + /// Returns the number of elements in the queue. + pub const fn len(&self) -> usize { self.length } - pub fn is_empty(&self) -> bool { + /// Returns `true` if the queue is empty. + pub const fn is_empty(&self) -> bool { self.len() == 0 } - pub fn is_full(&self) -> bool { - self.len() == Self::capacity() - } - - fn index(&self, i: usize) -> usize { - (self.start + i) % Self::capacity() + /// Returns `true` if the queue is full. + pub const fn is_full(&self) -> bool { + self.len() == N } - fn capacity() -> usize { - A::capacity() + const fn index(&self, index: usize) -> usize { + (self.start + index) % N } } -impl::Item]> + AsMut<[::Item]>> Clone for ArrayQueue -where - ::Item: Clone, -{ +impl Clone for ArrayQueue { fn clone(&self) -> Self { - let mut a = Self::new(); + let mut queue = Self::new(); for x in self { - a.push_back(x).unwrap(); + queue.push_back(x.clone()).unwrap(); } - a + queue } } -impl::Item]> + AsMut<[::Item]>> Default - for ArrayQueue -{ +impl Default for ArrayQueue { fn default() -> Self { - ArrayQueue::new() + Self::new() } } -impl::Item]> + AsMut<[::Item]>> Drop for ArrayQueue { +impl Drop for ArrayQueue { fn drop(&mut self) { - for x in self { - drop(replace(x, unsafe { uninitialized() })); - } + #[expect(clippy::redundant_pattern_matching)] + while let Some(_) = self.pop_back() {} } } -impl<'a, A: Array + AsRef<[::Item]> + AsMut<[::Item]>> IntoIterator - for &'a ArrayQueue -{ - type Item = &'a ::Item; - type IntoIter = ArrayQueueIterator<'a, A>; - - fn into_iter(self) -> Self::IntoIter { - let l = self.len(); +#[derive(Debug)] +pub struct ArrayQueueIterator<'a, T, const N: usize> { + queue: &'a ArrayQueue, + first: usize, + last: usize, +} - ArrayQueueIterator { - queue: self, - first: 0, - last: l, - } +impl<'a, T, const N: usize> ArrayQueueIterator<'a, T, N> { + const fn is_exhausted(&self) -> bool { + self.first >= self.last } } -impl<'a, A: Array + AsRef<[::Item]> + AsMut<[::Item]>> IntoIterator - for &'a mut ArrayQueue -{ - type Item = &'a mut ::Item; - type IntoIter = ArrayQueueMutIterator<'a, A>; +impl<'a, T, const N: usize> IntoIterator for &'a ArrayQueue { + type Item = &'a T; + type IntoIter = ArrayQueueIterator<'a, T, N>; fn into_iter(self) -> Self::IntoIter { - let l = self.len(); - - ArrayQueueMutIterator { + ArrayQueueIterator { queue: self, first: 0, - last: l, + last: self.len(), } } } -#[derive(Debug)] -pub struct ArrayQueueIterator< - 'a, - A: 'a + Array + AsRef<[::Item]> + AsMut<[::Item]>, -> { - queue: &'a ArrayQueue, - first: usize, - last: usize, -} - -impl<'a, A: 'a + Array + AsRef<[::Item]> + AsMut<[::Item]>> - ArrayQueueIterator<'a, A> -{ - fn exhausted(&self) -> bool { - self.first >= self.last - } -} - -impl<'a, A: Array + AsRef<[::Item]> + AsMut<[::Item]>> Iterator - for ArrayQueueIterator<'a, A> -{ - type Item = &'a ::Item; +impl<'a, T, const N: usize> Iterator for ArrayQueueIterator<'a, T, N> { + type Item = &'a T; fn next(&mut self) -> Option { - if self.exhausted() { + if self.is_exhausted() { return None; } - let x = &self.queue.array.as_ref()[self.queue.index(self.first)]; - self.first += 1; - Some(x) + let x = self.queue.element(self.first); + self.first += x.is_some() as usize; + x } } -impl<'a, A: Array + AsRef<[::Item]> + AsMut<[::Item]>> DoubleEndedIterator - for ArrayQueueIterator<'a, A> -{ +impl<'a, T, const N: usize> DoubleEndedIterator for ArrayQueueIterator<'a, T, N> { fn next_back(&mut self) -> Option { - if self.exhausted() { + if self.is_exhausted() { return None; } - self.last -= 1; - let x = &self.queue.array.as_ref()[self.queue.index(self.last)]; - Some(x) + let x = self.queue.element(self.last - 1); + self.last -= x.is_some() as usize; + x } } #[derive(Debug)] -pub struct ArrayQueueMutIterator< - 'a, - A: 'a + Array + AsRef<[::Item]> + AsMut<[::Item]>, -> { - queue: &'a mut ArrayQueue, +pub struct ArrayQueueMutIterator<'a, T, const N: usize> { + queue: &'a mut ArrayQueue, first: usize, last: usize, } -impl<'a, A: 'a + Array + AsRef<[::Item]> + AsMut<[::Item]>> - ArrayQueueMutIterator<'a, A> -{ - fn exhausted(&self) -> bool { +impl<'a, T, const N: usize> ArrayQueueMutIterator<'a, T, N> { + const fn is_exhausted(&self) -> bool { self.first >= self.last } } -impl<'a, A: Array + AsRef<[::Item]> + AsMut<[::Item]>> Iterator - for ArrayQueueMutIterator<'a, A> -{ - type Item = &'a mut ::Item; +impl<'a, T, const N: usize> IntoIterator for &'a mut ArrayQueue { + type Item = &'a mut T; + type IntoIter = ArrayQueueMutIterator<'a, T, N>; + + fn into_iter(self) -> Self::IntoIter { + let last = self.len(); + + ArrayQueueMutIterator { + queue: self, + first: 0, + last, + } + } +} + +impl<'a, T, const N: usize> Iterator for ArrayQueueMutIterator<'a, T, N> { + type Item = &'a mut T; fn next(&mut self) -> Option { - if self.exhausted() { + if self.is_exhausted() { return None; } - let i = self.queue.index(self.first); - let x = &mut self.queue.array.as_mut()[i] as *mut ::Item; - self.first += 1; - Some(unsafe { &mut *x }) + let x = self.queue.element_mut(self.first); + self.first += x.is_some() as usize; + // SAFETY: We do not modify the `queue` field during an iteration. + x.map(|x| unsafe { transmute(x) }) } } -impl<'a, A: Array + AsRef<[::Item]> + AsMut<[::Item]>> DoubleEndedIterator - for ArrayQueueMutIterator<'a, A> -{ +impl<'a, T, const N: usize> DoubleEndedIterator for ArrayQueueMutIterator<'a, T, N> { fn next_back(&mut self) -> Option { - if self.exhausted() { + if self.is_exhausted() { return None; } - self.last -= 1; - let i = self.queue.index(self.last); - let x = &mut self.queue.array.as_mut()[i] as *mut ::Item; - Some(unsafe { &mut *x }) + let x = self.queue.element_mut(self.last - 1); + self.last -= x.is_some() as usize; + // SAFETY: We do not modify the `queue` field during an iteration. + x.map(|x| unsafe { transmute(x) }) } } #[cfg(test)] mod test { use super::*; + use alloc::boxed::Box; #[test] fn new() { - ArrayQueue::<[usize; 1]>::new(); - ArrayQueue::<[usize; 2]>::new(); + ArrayQueue::::new(); + ArrayQueue::::new(); } #[test] fn first_and_last() { - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); assert_eq!(a.first(), None); assert_eq!(a.first_mut(), None); assert_eq!(a.last(), None); assert_eq!(a.last_mut(), None); - assert!(a.push_back(&1).is_ok()); + assert!(a.push_back(1).is_ok()); assert_eq!(a.first(), Some(&1)); assert_eq!(a.first_mut(), Some(&mut 1)); assert_eq!(a.last(), Some(&1)); assert_eq!(a.last_mut(), Some(&mut 1)); - assert!(a.push_back(&2).is_ok()); + assert!(a.push_back(2).is_ok()); assert_eq!(a.first(), Some(&1)); assert_eq!(a.first_mut(), Some(&mut 1)); @@ -335,63 +311,63 @@ mod test { #[test] fn push_back() { - let mut a: ArrayQueue<[usize; 1]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); assert_eq!(a.len(), 0); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(42).is_ok()); assert_eq!(a.len(), 1); - assert_eq!(a.push_back(&42), Err(CapacityError)); + assert_eq!(a.push_back(42), Err(CapacityError)); assert_eq!(a.len(), 1); - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); assert_eq!(a.len(), 0); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(42).is_ok()); assert_eq!(a.len(), 1); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(42).is_ok()); assert_eq!(a.len(), 2); - assert_eq!(a.push_back(&42), Err(CapacityError)); + assert_eq!(a.push_back(42), Err(CapacityError)); assert_eq!(a.len(), 2); } #[test] fn push_front() { - let mut a: ArrayQueue<[usize; 1]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); assert_eq!(a.len(), 0); - assert!(a.push_front(&42).is_ok()); + assert!(a.push_front(42).is_ok()); assert_eq!(a.len(), 1); - assert_eq!(a.push_front(&42), Err(CapacityError)); + assert_eq!(a.push_front(42), Err(CapacityError)); assert_eq!(a.len(), 1); - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); assert_eq!(a.len(), 0); - assert!(a.push_front(&1).is_ok()); + assert!(a.push_front(1).is_ok()); assert_eq!(a.first(), Some(&1)); assert_eq!(a.last(), Some(&1)); assert_eq!(a.len(), 1); - assert!(a.push_front(&2).is_ok()); + assert!(a.push_front(2).is_ok()); assert_eq!(a.first(), Some(&2)); assert_eq!(a.last(), Some(&1)); assert_eq!(a.len(), 2); - assert_eq!(a.push_front(&3), Err(CapacityError)); + assert_eq!(a.push_front(3), Err(CapacityError)); assert_eq!(a.len(), 2); } #[test] fn pop_back() { - let mut a: ArrayQueue<[usize; 1]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(42).is_ok()); assert_eq!(a.pop_back(), Some(42)); assert_eq!(a.len(), 0); - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&123).is_ok()); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(123).is_ok()); + assert!(a.push_back(42).is_ok()); assert_eq!(a.pop_back(), Some(42)); assert_eq!(a.first(), Some(&123)); @@ -403,17 +379,17 @@ mod test { #[test] fn pop_front() { - let mut a: ArrayQueue<[usize; 1]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(42).is_ok()); assert_eq!(a.pop_front(), Some(42)); assert_eq!(a.len(), 0); - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&123).is_ok()); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(123).is_ok()); + assert!(a.push_back(42).is_ok()); assert_eq!(a.pop_front(), Some(123)); assert_eq!(a.first(), Some(&42)); @@ -425,46 +401,46 @@ mod test { #[test] fn push_and_pop_across_edges() { - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&1).is_ok()); - assert!(a.push_back(&2).is_ok()); + assert!(a.push_back(1).is_ok()); + assert!(a.push_back(2).is_ok()); for i in 3..64 { assert_eq!(a.pop_front(), Some(i - 2)); assert_eq!(a.len(), 1); - assert!(a.push_back(&i).is_ok()); + assert!(a.push_back(i).is_ok()); assert_eq!(a.len(), 2); } } #[test] fn is_empty() { - let a: ArrayQueue<[usize; 1]> = ArrayQueue::new(); + let a: ArrayQueue = ArrayQueue::new(); assert!(a.is_empty()); - let a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let a: ArrayQueue = ArrayQueue::new(); assert!(a.is_empty()); } #[test] fn is_full() { - let mut a: ArrayQueue<[usize; 1]> = ArrayQueue::new(); - assert!(a.push_back(&0).is_ok()); + let mut a: ArrayQueue = ArrayQueue::new(); + assert!(a.push_back(0).is_ok()); assert!(a.is_full()); - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); - assert!(a.push_back(&0).is_ok()); - assert!(a.push_back(&0).is_ok()); + let mut a: ArrayQueue = ArrayQueue::new(); + assert!(a.push_back(0).is_ok()); + assert!(a.push_back(0).is_ok()); assert!(a.is_full()); } #[test] fn iterator() { - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&0).is_ok()); - assert!(a.push_back(&1).is_ok()); + assert!(a.push_back(0).is_ok()); + assert!(a.push_back(1).is_ok()); for (i, e) in a.into_iter().enumerate() { assert_eq!(*e, i); @@ -473,12 +449,12 @@ mod test { #[test] fn iterator_across_edges() { - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&42).is_ok()); + assert!(a.push_back(42).is_ok()); a.pop_front(); - assert!(a.push_back(&0).is_ok()); - assert!(a.push_back(&1).is_ok()); + assert!(a.push_back(0).is_ok()); + assert!(a.push_back(1).is_ok()); for (i, e) in a.into_iter().enumerate() { assert_eq!(*e, i); @@ -487,10 +463,10 @@ mod test { #[test] fn iterate_forward_and_backward() { - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a = ArrayQueue::::new(); - assert!(a.push_back(&0).is_ok()); - assert!(a.push_back(&1).is_ok()); + assert!(a.push_back(0).is_ok()); + assert!(a.push_back(1).is_ok()); let mut i = a.into_iter(); @@ -501,11 +477,11 @@ mod test { } #[test] - fn iterate_forward_and_backward_mutablly() { - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + fn iterate_forward_and_backward_mutable() { + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&0).is_ok()); - assert!(a.push_back(&1).is_ok()); + assert!(a.push_back(0).is_ok()); + assert!(a.push_back(1).is_ok()); let mut i = (&mut a).into_iter(); @@ -517,17 +493,17 @@ mod test { #[test] fn iterate_empty_queue() { - let a = ArrayQueue::<[usize; 0]>::new(); + let a = ArrayQueue::::new(); for _ in a.into_iter() {} } #[test] fn iterator_mut() { - let mut a: ArrayQueue<[usize; 2]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - assert!(a.push_back(&0).is_ok()); - assert!(a.push_back(&1).is_ok()); + assert!(a.push_back(0).is_ok()); + assert!(a.push_back(1).is_ok()); for (i, e) in (&mut a).into_iter().enumerate() { assert_eq!(*e, i); @@ -537,20 +513,20 @@ mod test { #[test] fn reference_elements() { - let mut a: ArrayQueue<[Box; 2]> = ArrayQueue::new(); - assert!(a.push_back(&Box::new(42)).is_ok()); - assert!(a.push_front(&Box::new(42)).is_ok()); + let mut a: ArrayQueue, 2> = ArrayQueue::new(); + assert!(a.push_back(Box::new(42)).is_ok()); + assert!(a.push_front(Box::new(42)).is_ok()); } #[test] fn clone() { - let mut a: ArrayQueue<[Box; 32]> = ArrayQueue::new(); + let mut a: ArrayQueue, 32> = ArrayQueue::new(); for _ in 0..32 { - assert!(a.push_back(&Box::new(42)).is_ok()); + assert!(a.push_back(Box::new(42)).is_ok()); } - a.clone(); + let _ = a.clone(); } static mut FOO_SUM: usize = 0; @@ -570,17 +546,17 @@ mod test { fn no_drops_of_elements_on_push_back() { assert_eq!(unsafe { FOO_SUM }, 0); - let mut a: ArrayQueue<[Foo; 32]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); - for _ in 0..32 { - assert!(a.push_back(&Foo).is_ok()); + for _ in 0..17 { + assert!(a.push_back(Foo).is_ok()); } - assert_eq!(unsafe { FOO_SUM }, 32); // drops of arguments `&Foo` + assert_eq!(unsafe { FOO_SUM }, 0); drop(a); - assert_eq!(unsafe { FOO_SUM }, 64); // drops of elements + assert_eq!(unsafe { FOO_SUM }, 17); } static mut BAR_SUM: usize = 0; @@ -600,22 +576,22 @@ mod test { fn drops_of_elements_on_pop_back() { assert_eq!(unsafe { BAR_SUM }, 0); - let mut a: ArrayQueue<[Bar; 32]> = ArrayQueue::new(); + let mut a: ArrayQueue = ArrayQueue::new(); for _ in 0..32 { - assert!(a.push_back(&Bar).is_ok()); + assert!(a.push_back(Bar).is_ok()); } - assert_eq!(unsafe { BAR_SUM }, 32); // drops of arguments `&Bar` + assert_eq!(unsafe { BAR_SUM }, 0); for _ in 0..32 { assert!(a.pop_back().is_some()); } - assert_eq!(unsafe { BAR_SUM }, 64); // drops of elements + assert_eq!(unsafe { BAR_SUM }, 32); drop(a); - assert_eq!(unsafe { BAR_SUM }, 64); + assert_eq!(unsafe { BAR_SUM }, 32); } } diff --git a/src/error.rs b/src/error.rs index a4a97d6..2ddd601 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,14 +1,14 @@ -use std::error::Error; -use std::fmt::{Display, Formatter, Result}; +use core::error::Error; +use core::fmt::{Display, Formatter, Result}; -const MESSAGE: &'static str = "queue is full"; +const MESSAGE: &str = "queue is full"; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct CapacityError; impl Display for CapacityError { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}", MESSAGE) + write!(f, "{MESSAGE}") } } diff --git a/src/lib.rs b/src/lib.rs index 898f7fb..729e819 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,9 @@ -extern crate arrayvec; +//! A queue backed by a fixed-size array. + +#![no_std] + +#[cfg(test)] +extern crate alloc; mod array_queue; mod error;