use core::marker::Destruct;
use core::mem::MaybeUninit;
use core::ops;
use super::ArrayVec;
pub(crate) const fn write_filled_const<T: [const] Clone + [const] Destruct>(
slice: &mut [MaybeUninit<T>],
value: T,
) {
if slice.is_empty() {
return;
}
konst::iter::for_each! { elem in konst::slice::get_up_to_mut(slice, slice.len().wrapping_sub(1)).unwrap() =>
elem.write(value.clone());
}
slice[slice.len().wrapping_sub(1)].write(value);
}
impl<const C: usize, T> const ops::Deref for ArrayVec<C, T> {
type Target = [T];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<const C: usize, T> const ops::DerefMut for ArrayVec<C, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_slice_mut()
}
}
impl<const C: usize, T> const ops::Index<usize> for ArrayVec<C, T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.as_slice()[index]
}
}
impl<const C: usize, T> const ops::IndexMut<usize> for ArrayVec<C, T> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.as_slice_mut()[index]
}
}
impl<const C: usize, T> const Default for ArrayVec<C, T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<const C: usize, T: PartialEq> PartialEq for ArrayVec<C, T> {
#[inline]
default fn eq(&self, other: &ArrayVec<C, T>) -> bool {
self.len() == other.len() && self.as_slice().eq(other.as_slice())
}
}
impl<const C: usize, T: [const] PartialEq> const PartialEq for ArrayVec<C, T> {
#[inline]
fn eq(&self, other: &ArrayVec<C, T>) -> bool {
self.len() == other.len() && self.as_slice().eq(other.as_slice())
}
}
impl<const C: usize, T: [const] Eq> const Eq for ArrayVec<C, T> {}
impl<const C: usize, T: PartialOrd> PartialOrd for ArrayVec<C, T> {
#[inline]
default fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.as_slice().partial_cmp(other.as_slice())
}
}
impl<const C: usize, T: [const] PartialOrd> const PartialOrd for ArrayVec<C, T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.as_slice().partial_cmp(other.as_slice())
}
}
impl<const C: usize, T: Ord> Ord for ArrayVec<C, T> {
#[inline]
default fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl<const C: usize, T: [const] Ord> const Ord for ArrayVec<C, T> {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl<const C: usize, T> ArrayVec<C, T> {
#[inline]
pub const fn from_slice_const(slice: &[T]) -> Option<Self>
where
T: const Clone, {
if slice.len() > C {
return None;
}
let mut array = konst::maybe_uninit::UNINIT_ARRAY::<T, C>::V;
konst::iter::for_each! {idx in 0..slice.len() =>
let item = slice[idx].clone();
array[idx] = MaybeUninit::new(item);
}
Some(Self {
array,
len: slice.len(),
})
}
#[inline]
pub const fn truncate_const(&mut self, len: usize)
where
T: [const] Destruct, {
if len >= self.len {
return;
}
unsafe { self.array[len..self.len].assume_init_drop() };
self.len = len;
}
#[inline]
pub const fn resize_const(&mut self, new_len: usize, value: T)
where
T: [const] Clone + [const] Destruct, {
assert!(new_len <= C, "Tried to resize beyond capacity.");
if new_len < self.len {
self.truncate_const(new_len);
} else if new_len > self.len {
write_filled_const(&mut self.array[self.len..new_len], value);
self.len = new_len;
}
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use proptest::prelude::*;
fn proptest_config() -> ProptestConfig {
ProptestConfig {
#[cfg(miri)]
failure_persistence: None,
#[cfg(miri)]
cases: 32,
..ProptestConfig::default()
}
}
#[test]
fn const_partial_eq() {
static A: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 3]);
static B: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 3]);
static C: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 4]);
assert_eq!(A, B);
assert_ne!(A, C);
}
#[test]
fn const_ord() {
static A: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 3]);
static B: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 4]);
assert!(A < B);
}
#[test]
fn write_filled_const_empty() {
let mut vec: ArrayVec<0, ()> = ArrayVec::default();
write_filled_const(&mut vec.array, ());
assert!(vec.is_empty());
}
#[test]
fn from_slice_const() {
const EXPECTED: [u32; 4] = [67, 69, 420, 80085];
let vec: ArrayVec<4, u32> = ArrayVec::<4, _>::from_slice_const(&EXPECTED).unwrap();
assert_eq!(vec.as_slice(), EXPECTED);
}
#[test]
fn from_slice_overflow() {
assert!(ArrayVec::<4, u32>::from_slice_const(&[1; 8]).is_none());
}
#[test]
fn truncate_const() {
let mut vec = ArrayVec::from_array([1, 2, 3, 4]);
vec.truncate_const(2);
assert_eq!(vec.as_slice(), [1, 2]);
vec.push(0);
assert_eq!(vec.as_slice(), [1, 2, 0]);
vec.truncate_const(0);
assert_eq!(vec.as_slice(), []);
vec.truncate_const(0xcafebabe);
assert!(vec.is_empty());
}
#[test]
fn truncate_const_heap_types() {
let mut vec = ArrayVec::from_array([String::from("hey"), String::from("boy")]);
vec.truncate_const(1);
assert_eq!(vec.as_slice(), [String::from("hey")]);
core::mem::drop(vec);
}
#[test]
fn truncate_const_noop() {
let mut vec = ArrayVec::from_array([1, 2, 3, 4]);
vec.truncate_const(4);
assert_eq!(vec.len(), 4);
vec.truncate_const(10);
assert_eq!(vec.len(), 4);
}
#[derive(Debug, Clone)]
enum Op {
Push(u8),
TryPush(u8),
Pop,
Insert(u8, usize),
TryInsert(u8, usize),
Truncate(usize),
Resize(usize, u8),
TruncateConst(usize),
ResizeConst(usize, u8),
}
fn arbatrary_op() -> impl Strategy<Value = Op> {
prop_oneof![
any::<u8>().prop_map(Op::Push),
any::<u8>().prop_map(Op::TryPush),
Just(Op::Pop),
(any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::Insert(v, i)),
(any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::TryInsert(v, i)),
any::<usize>().prop_map(Op::Truncate),
(any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::Resize(i, v)),
any::<usize>().prop_map(Op::TruncateConst),
(any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::ResizeConst(i, v)),
]
}
fn model_try_push(v: &mut Vec<u8>, value: u8) -> Result<(), u8> {
if v.len() >= v.capacity() {
return Err(value);
}
v.push(value);
Ok(())
}
fn model_try_insert(v: &mut Vec<u8>, value: u8, idx: usize) -> Result<(), u8> {
if v.len() >= v.capacity() || idx > v.len() {
return Err(value);
}
v.insert(idx, value);
Ok(())
}
proptest! {
#![proptest_config(proptest_config())]
#[test]
fn model_based(ops in prop::collection::vec(arbatrary_op(), 0..50)) {
const C: usize = 8;
let mut vec: ArrayVec<C, u8> = ArrayVec::new();
let mut model: Vec<u8> = Vec::with_capacity(C);
for op in ops {
match op {
Op::Push(v) => {
if model.len() < C {
vec.push(v);
model.push(v);
}
}
Op::TryPush(x) => {
assert_eq!(vec.try_push(x), model_try_push(&mut model, x));
}
Op::Pop => {
assert_eq!(vec.pop(), model.pop());
}
Op::Insert(v, i) => {
if model.len() < C {
let idx = i % (model.len() + 1);
vec.insert(v, idx);
model.insert(idx, v);
}
}
Op::TryInsert(val, idx) => {
assert_eq!(
vec.try_insert(val, idx),
model_try_insert(&mut model, val, idx)
);
}
Op::Truncate(n) => {
let n = n % (C + 1);
vec.truncate(n);
model.truncate(n);
}
Op::Resize(n, x) => {
let n = n % (C + 1);
vec.resize(n, x);
model.resize(n,x);
}
Op::TruncateConst(n) => {
let n = n % (C + 1);
vec.truncate_const(n);
model.truncate(n);
}
Op::ResizeConst(n, x) => {
let n = n % (C + 1);
vec.resize_const(n, x);
model.resize(n,x);
}
}
assert_eq!(vec.as_slice(), model.as_slice());
assert!(vec.len() <= C);
}
}
}
}