diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 619639c..27b5292 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,7 +91,6 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} files: lcov.info - fail_ci_if_error: true validate: name: wait for jobs diff --git a/README.md b/README.md index 6d30a20..f76dc9f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# StaticVector +# Static Vector [![license](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![build](https://github.com/andreiavrammsd/static_vector.rs/workflows/CI/badge.svg)](https://github.com/andreiavrammsd/static_vector.rs/actions/workflows/ci.yml) @@ -6,7 +6,7 @@ A no-std, stack-allocated vector with fixed capacity and dynamic length. -[`StaticVector`] stores elements on the stack using a fixed-size array without heap allocations. +[`Vec`] stores elements on the stack using a fixed-size array without heap allocations. Aims to be suitable for low-level projects and to have an API as safe and explicit as possible. The goal is to allocate only when needed. When first constructed, the vector will not allocate. @@ -21,8 +21,8 @@ The goal is to allocate only when needed. When first constructed, the vector wil ## Requirements -- `T: Clone` for insertion: [`StaticVector::push()`] -- `T: Default` only if [`StaticVector::set_len()`] is used +- `T: Clone` for insertion: [`Vec::push()`] +- `T: Default` only if [`Vec::set_len()`] is used - `CAPACITY > 0` ## Complexity @@ -37,9 +37,9 @@ All operations are O(1) except: ## Example ```rust -use static_vector::StaticVector; +use static_vector::Vec; -let mut vec = StaticVector::::new(); +let mut vec = Vec::::new(); vec.push(&4).unwrap(); vec.push(&5).unwrap(); diff --git a/benches/static_vector.rs b/benches/static_vector.rs index eea1a4c..5fecfdc 100644 --- a/benches/static_vector.rs +++ b/benches/static_vector.rs @@ -1,8 +1,8 @@ use criterion::{Criterion, criterion_group, criterion_main}; -use static_vector::StaticVector; +use static_vector::Vec; fn bench_static_vector(c: &mut Criterion) { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); c.bench_function("push and clear", |b| { b.iter(|| { diff --git a/codecov.yml b/codecov.yml index 36bbd8a..afd9925 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,7 +2,11 @@ coverage: status: patch: default: - threshold: 100 + target: 100% + threshold: 0% + if_ci_failed: error project: default: - threshold: 100 + target: 100% + threshold: 0% + if_ci_failed: error diff --git a/src/lib.rs b/src/lib.rs index 0f90248..cd6e2a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,35 +4,51 @@ use core::{array, mem::MaybeUninit}; -/// Error type returned by [`StaticVector`]. +/// Attempted to push to a full vector #[derive(Debug)] -pub enum Error { - /// Attempted to push to a full vector. - CapacityExceeded, +#[non_exhaustive] +pub struct CapacityExceededError; - /// Attempted to resize the vector to a length greater than its fixed capacity. - LengthTooLarge, +impl core::fmt::Display for CapacityExceededError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("attempted to push to a full vector") + } } +impl core::error::Error for CapacityExceededError {} + +/// Attempted to resize the vector to a length greater than its fixed capacity. +#[derive(Debug)] +#[non_exhaustive] +pub struct LengthTooLargeError; + +impl core::fmt::Display for LengthTooLargeError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("attempted to resize the vector to a length greater than its fixed capacity") + } +} + +impl core::error::Error for LengthTooLargeError {} + /// A stack-allocated vector with fixed capacity and dynamic length. /// /// See crate-level documentation for details and usage. -pub struct StaticVector { +pub struct Vec { data: [MaybeUninit; CAPACITY], length: usize, } -impl Default for StaticVector { - /// Creates an empty [`StaticVector`]. Equivalent to [`StaticVector::new()`]. +impl Default for Vec { + /// Creates an empty [`Vec`]. Equivalent to [`Vec::new()`]. fn default() -> Self { Self::new() } } -impl StaticVector { +impl Vec { const ASSERT_CAPACITY: () = assert!(CAPACITY > 0); - /// Creates a new empty [`StaticVector`] with maximum `CAPACITY` elements of type `T`. + /// Creates a new empty [`Vec`] with maximum `CAPACITY` elements of type `T`. #[inline] pub fn new() -> Self { let () = Self::ASSERT_CAPACITY; @@ -62,10 +78,10 @@ impl StaticVector { /// /// # Errors /// - /// Returns [`Error::CapacityExceeded`] if the vector is already at full capacity. - pub fn push(&mut self, value: &T) -> Result<(), Error> { + /// Returns [`CapacityExceededError`] if the vector is already at full capacity. + pub fn push(&mut self, value: &T) -> Result<(), CapacityExceededError> { if self.length == CAPACITY { - return Err(Error::CapacityExceeded); + return Err(CapacityExceededError); } self.data[self.length].write(value.clone()); @@ -89,13 +105,13 @@ impl StaticVector { /// /// # Errors /// - /// Returns [`Error::LengthTooLarge`] if `new_length` exceeds the vector's fixed capacity. - pub fn set_len(&mut self, new_length: usize) -> Result<(), Error> + /// Returns [`LengthTooLargeError`] if `new_length` exceeds the vector's fixed capacity. + pub fn set_len(&mut self, new_length: usize) -> Result<(), LengthTooLargeError> where T: Default, { if new_length > CAPACITY { - return Err(Error::LengthTooLarge); + return Err(LengthTooLargeError); } if new_length > self.length { @@ -142,14 +158,14 @@ impl StaticVector { /// Returns an iterator over immutable references to the elements in the vector. #[inline(always)] - pub fn iter(&self) -> StaticVectorIterator { - StaticVectorIterator { data: &self.data, size: self.length, index: 0 } + pub fn iter(&self) -> Iter { + Iter::new(&self.data, self.length) } /// Returns an iterator over mutable references to the elements in the vector. #[inline(always)] - pub fn iter_mut(&mut self) -> StaticVectorMutableIterator { - StaticVectorMutableIterator { data: &mut self.data, size: self.length, index: 0 } + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(&mut self.data, self.length) } fn drop(&mut self, from: usize, to: usize) { @@ -161,23 +177,31 @@ impl StaticVector { } } -impl Drop for StaticVector { +impl Drop for Vec { fn drop(&mut self) { self.drop(0, self.length); } } -/// Immutable iterator over a [`StaticVector`]. +/// Immutable iterator over a [`Vec`]. /// -/// Created by calling [`StaticVector::iter()`]. +/// Created by calling [`Vec::iter()`]. #[must_use = "must consume iterator"] -pub struct StaticVectorIterator<'a, T> { +pub struct Iter<'a, T> { data: &'a [MaybeUninit], size: usize, index: usize, } -impl<'a, T> Iterator for StaticVectorIterator<'a, T> { +impl<'a, T> Iter<'a, T> { + /// Creates immutable iterator. + #[inline(always)] + pub fn new(data: &'a [MaybeUninit], size: usize) -> Self { + Self { data, size, index: 0 } + } +} + +impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { @@ -191,17 +215,25 @@ impl<'a, T> Iterator for StaticVectorIterator<'a, T> { } } -/// Mutable iterator over a [`StaticVector`]. +/// Mutable iterator over a [`Vec`]. /// -/// Created by calling [`StaticVector::iter_mut()`]. +/// Created by calling [`Vec::iter_mut()`]. #[must_use = "must consume iterator"] -pub struct StaticVectorMutableIterator<'a, T> { +pub struct IterMut<'a, T> { data: &'a mut [MaybeUninit], size: usize, index: usize, } -impl<'a, T> Iterator for StaticVectorMutableIterator<'a, T> { +impl<'a, T> IterMut<'a, T> { + /// Creates mutable iterator. + #[inline(always)] + pub fn new(data: &'a mut [MaybeUninit], size: usize) -> Self { + Self { data, size, index: 0 } + } +} + +impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option { @@ -220,20 +252,22 @@ mod tests { use super::*; extern crate std; - use std::{cell::Cell, thread_local}; + use std::{cell::Cell, format, thread_local}; + + fn assert_is_core_error() {} #[test] fn construct() { - assert!(StaticVector::::new().is_empty()); - assert!(StaticVector::::default().is_empty()); + assert!(Vec::::new().is_empty()); + assert!(Vec::::default().is_empty()); // Will not build because CAPACITY must be greater than zero - // StaticVector::::new().is_empty(); + // Vec::::new().is_empty(); } #[test] fn capacity() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); assert_eq!(vec.capacity(), 3); @@ -246,10 +280,13 @@ mod tests { #[test] fn push() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); assert!(vec.push(&1).is_ok()); assert!(vec.push(&2).is_ok()); - assert!(matches!(vec.push(&3), Err(Error::CapacityExceeded))); + + assert!(matches!(vec.push(&3), Err(CapacityExceededError))); + assert_eq!(format!("{}", vec.push(&3).unwrap_err()), "attempted to push to a full vector"); + assert_is_core_error::(); assert_eq!(vec.get(0).unwrap(), &1); assert_eq!(vec.get(1).unwrap(), &2); @@ -258,7 +295,7 @@ mod tests { #[test] fn size() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); assert_eq!(vec.len(), 0); assert!(vec.is_empty()); @@ -271,7 +308,12 @@ mod tests { assert_eq!(vec.len(), 1); assert!(!vec.is_empty()); - assert!(matches!(vec.set_len(100), Err(Error::LengthTooLarge))); + assert!(matches!(vec.set_len(100), Err(LengthTooLargeError))); + assert_eq!( + format!("{}", vec.set_len(100).unwrap_err()), + "attempted to resize the vector to a length greater than its fixed capacity" + ); + assert_is_core_error::(); vec.clear(); assert_eq!(vec.len(), 0); @@ -280,7 +322,7 @@ mod tests { #[test] fn get() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); assert!(vec.first().is_none()); assert!(vec.last().is_none()); assert!(vec.get(0).is_none()); @@ -308,7 +350,7 @@ mod tests { #[test] fn iter() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); for i in 1..8 { vec.push(&i).unwrap() } @@ -319,7 +361,7 @@ mod tests { #[test] fn iter_mut() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); for i in 1..8 { vec.push(&i).unwrap() } @@ -358,20 +400,20 @@ mod tests { #[test] fn construct_should_not_create_default_elements() { - let _ = StaticVector::::new(); + let _ = Vec::::new(); assert_eq!(DEFAULTS.get(), 0); } #[test] fn push_should_not_create_default_elements() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); vec.push(&Struct {}).unwrap(); assert_eq!(DEFAULTS.get(), 0); } #[test] fn set_len_should_create_default_elements() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); // Length zero, no defaults vec.set_len(0).unwrap(); @@ -398,7 +440,7 @@ mod tests { #[test] fn push_should_clone_element() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); vec.push(&Struct {}).unwrap(); assert_eq!(CLONES.get(), 1); @@ -410,7 +452,7 @@ mod tests { #[test] fn clear_should_drop_all_allocated_elements() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); assert_eq!(DROPS.get(), 0); let s = Struct::default(); @@ -425,7 +467,7 @@ mod tests { #[test] fn set_len_should_drop_all_allocated_elements() { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); assert_eq!(DROPS.get(), 0); let s = Struct::default(); @@ -461,7 +503,7 @@ mod tests { let s = Struct::default(); { - let mut vec = StaticVector::::new(); + let mut vec = Vec::::new(); assert_eq!(DROPS.get(), 0); for _ in 1..4 {