use core::{
convert::TryFrom,
fmt::{self, Debug, Display, Formatter},
marker::PhantomData,
};
use alloc::{vec, vec::Vec};
use scale_info::{
scale::{Decode, Encode},
TypeInfo,
};
#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct LimitedVec<T, E, const N: usize>(Vec<T>, PhantomData<E>);
impl<T: Clone + Default, E: Default, const N: usize> Display for LimitedVec<T, E, N>
where
[T]: AsRef<[u8]>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let len = self.0.len();
let median = (len + 1) / 2;
let mut e1 = median;
let mut s2 = median;
if let Some(precision) = f.precision() {
if precision < median {
e1 = precision;
s2 = len - precision;
}
} else if !f.sign_plus() && median > 8 {
e1 = 8;
s2 = len - 8;
}
let p1 = hex::encode(&self.0[..e1]);
let p2 = hex::encode(&self.0[s2..]);
let sep = e1.ne(&s2).then_some("..").unwrap_or_default();
if f.alternate() {
write!(f, "LimitedVec(0x{p1}{sep}{p2})")
} else {
write!(f, "0x{p1}{sep}{p2}")
}
}
}
impl<T: Clone + Default, E: Default, const N: usize> Debug for LimitedVec<T, E, N>
where
[T]: AsRef<[u8]>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
core::fmt::Display::fmt(self, f)
}
}
impl<T, E: Default, const N: usize> TryFrom<Vec<T>> for LimitedVec<T, E, N> {
type Error = E;
fn try_from(x: Vec<T>) -> Result<Self, Self::Error> {
(x.len() <= N).then_some(()).ok_or_else(E::default)?;
Ok(Self(x, PhantomData))
}
}
impl<T: Clone + Default, E: Default, const N: usize> LimitedVec<T, E, N> {
pub const fn new() -> Self {
Self(Vec::new(), PhantomData)
}
pub fn try_new_default(len: usize) -> Result<Self, E> {
(len <= N).then_some(()).ok_or_else(E::default)?;
Ok(Self(vec![T::default(); len], PhantomData))
}
pub fn new_default() -> Self {
Self(vec![T::default(); N], PhantomData)
}
pub fn filled_with(value: T) -> Self {
Self(vec![value; N], PhantomData)
}
pub fn extend_with(&mut self, value: T) {
self.0.resize(N, value);
}
pub fn try_push(&mut self, value: T) -> Result<(), E> {
(self.0.len() != N).then_some(()).ok_or_else(E::default)?;
self.0.push(value);
Ok(())
}
pub fn try_extend_from_slice(&mut self, values: &[T]) -> Result<(), E> {
self.0
.len()
.checked_add(values.len())
.and_then(|len| (len <= N).then_some(()))
.ok_or_else(E::default)?;
self.0.extend_from_slice(values);
Ok(())
}
pub fn try_prepend(&mut self, values: Self) -> Result<(), E> {
self.0
.len()
.checked_add(values.0.len())
.and_then(|len| (len <= N).then_some(()))
.ok_or_else(E::default)?;
self.0.splice(0..0, values.0);
Ok(())
}
pub fn inner(&self) -> &[T] {
&self.0
}
pub fn inner_mut(&mut self) -> &mut [T] {
&mut self.0
}
pub fn into_vec(self) -> Vec<T> {
self.0
}
pub const fn max_len() -> usize {
N
}
}
const RUNTIME_MAX_ALLOC_SIZE: usize = 512 * 0x10000;
const RUNTIME_MAX_BUFF_SIZE: usize = RUNTIME_MAX_ALLOC_SIZE / 2;
#[derive(
Clone, Copy, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo,
)]
pub struct RuntimeBufferSizeError;
impl From<RuntimeBufferSizeError> for &str {
fn from(_: RuntimeBufferSizeError) -> Self {
"Runtime buffer size exceed"
}
}
impl Display for RuntimeBufferSizeError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str((*self).into())
}
}
pub type RuntimeBuffer = LimitedVec<u8, RuntimeBufferSizeError, RUNTIME_MAX_BUFF_SIZE>;
#[cfg(test)]
mod test {
use super::{LimitedVec, RuntimeBufferSizeError};
use alloc::{string::String, vec, vec::Vec};
use core::convert::{TryFrom, TryInto};
const N: usize = 1000;
type TestBuffer = LimitedVec<u8, RuntimeBufferSizeError, N>;
const M: usize = 64;
type SmallTestBuffer = LimitedVec<u8, RuntimeBufferSizeError, M>;
#[test]
fn test_try_from() {
let v1 = vec![1; N];
let v2 = vec![1; N + 1];
let v3 = vec![1; N - 1];
let x = TestBuffer::try_from(v1).unwrap();
let _ = TestBuffer::try_from(v2).expect_err("Must be err because of size overflow");
let z = TestBuffer::try_from(v3).unwrap();
assert_eq!(x.inner().len(), N);
assert_eq!(z.inner().len(), N - 1);
assert_eq!(x.inner()[N / 2], 1);
assert_eq!(z.inner()[N / 2], 1);
}
#[test]
fn test_new_default() {
let x = LimitedVec::<String, RuntimeBufferSizeError, N>::try_new_default(N).unwrap();
assert!(
LimitedVec::<u64, RuntimeBufferSizeError, N>::try_new_default(N + 1).is_err(),
"Must be error because of size overflow"
);
let z = LimitedVec::<Vec<u8>, RuntimeBufferSizeError, N>::try_new_default(0).unwrap();
assert_eq!(x.inner().len(), N);
assert_eq!(z.inner().len(), 0);
assert_eq!(x.inner()[N / 2], "");
}
#[test]
fn test_prepend_works() {
let mut buf = TestBuffer::try_from(vec![1, 2, 3, 4, 5]).unwrap();
let prepend_buf = TestBuffer::try_from(vec![6, 7, 8]).unwrap();
buf.try_prepend(prepend_buf).unwrap();
assert_eq!(buf.inner(), &[6, 7, 8, 1, 2, 3, 4, 5]);
}
#[test]
fn test_full() {
let mut x = TestBuffer::try_from(vec![1; N]).unwrap();
let mut y = TestBuffer::try_from(vec![2; N / 2]).unwrap();
let mut z = TestBuffer::try_from(vec![3; 0]).unwrap();
x.try_push(42).unwrap_err();
y.try_push(42).unwrap();
z.try_push(42).unwrap();
x.try_extend_from_slice(&[1, 2, 3]).unwrap_err();
y.try_extend_from_slice(&[1, 2, 3]).unwrap();
z.try_extend_from_slice(&[1, 2, 3]).unwrap();
x.try_prepend(vec![1, 2, 3].try_into().unwrap())
.unwrap_err();
y.try_prepend(vec![1, 2, 3].try_into().unwrap()).unwrap();
z.try_prepend(vec![1, 2, 3].try_into().unwrap()).unwrap();
z.inner_mut()[0] = 0;
assert_eq!(&z.into_vec(), &[0, 2, 3, 42, 1, 2, 3]);
assert_eq!(TestBuffer::max_len(), N);
}
#[test]
fn formatting_test() {
use alloc::format;
let buffer = SmallTestBuffer::try_from(b"abcdefghijklmnopqrstuvwxyz012345".to_vec())
.expect("String is 64 bytes");
assert_eq!(
format!("{buffer:+?}"),
"0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435"
);
assert_eq!(
format!("{buffer:?}"),
"0x6162636465666768..797a303132333435"
);
assert_eq!(format!("{buffer:.0?}"), "0x..");
assert_eq!(format!("{buffer:.1?}"), "0x61..35");
assert_eq!(format!("{buffer:.2?}"), "0x6162..3435");
assert_eq!(format!("{buffer:.4?}"), "0x61626364..32333435");
assert_eq!(
format!("{buffer:.15?}"),
"0x6162636465666768696a6b6c6d6e6f..72737475767778797a303132333435"
);
assert_eq!(
format!("{buffer:.30?}"),
"0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435"
);
assert_eq!(
format!("{buffer:#}"),
"LimitedVec(0x6162636465666768..797a303132333435)"
);
assert_eq!(
format!("{buffer:+#}"),
"LimitedVec(0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435)"
);
assert_eq!(format!("{buffer:#.2}"), "LimitedVec(0x6162..3435)");
}
}