use core::borrow::{Borrow, BorrowMut};
use core::fmt::{self, Display, Formatter, LowerHex, UpperHex};
use core::str::FromStr;
use core::hash::Hash;
use core::ops::{
Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
use alloc::vec::Vec;
use alloc::string::String;
use alloc::borrow::ToOwned;
use alloc::collections::{btree_map, BTreeMap, BTreeSet, VecDeque};
use core::slice::SliceIndex;
#[cfg(feature = "std")]
use std::{
io, usize,
collections::{hash_map, HashMap, HashSet},
};
use amplify_num::hex;
use amplify_num::hex::{FromHex, ToHex};
use ascii::{AsAsciiStrError, AsciiChar, AsciiString};
use crate::num::u24;
use crate::Wrapper;
pub trait Collection: Extend<Self::Item> {
type Item;
fn with_capacity(capacity: usize) -> Self;
fn len(&self) -> usize;
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0
}
fn push(&mut self, elem: Self::Item);
fn clear(&mut self);
}
pub trait KeyedCollection: Collection<Item = (Self::Key, Self::Value)> {
type Key: Eq + Hash;
type Value;
fn get_mut(&mut self, key: &Self::Key) -> Option<&mut Self::Value>;
fn insert(&mut self, key: Self::Key, value: Self::Value) -> Option<Self::Value>;
fn remove(&mut self, key: &Self::Key) -> Option<Self::Value>;
}
impl Collection for String {
type Item = char;
fn with_capacity(capacity: usize) -> Self {
Self::with_capacity(capacity)
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
self.push(elem)
}
fn clear(&mut self) {
self.clear()
}
}
impl Collection for AsciiString {
type Item = AsciiChar;
fn with_capacity(capacity: usize) -> Self {
Self::with_capacity(capacity)
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
self.push(elem)
}
fn clear(&mut self) {
self.clear()
}
}
impl<T> Collection for Vec<T> {
type Item = T;
fn with_capacity(capacity: usize) -> Self {
Self::with_capacity(capacity)
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
self.push(elem)
}
fn clear(&mut self) {
self.clear()
}
}
impl<T> Collection for VecDeque<T> {
type Item = T;
fn with_capacity(capacity: usize) -> Self {
Self::with_capacity(capacity)
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
self.push_back(elem)
}
fn clear(&mut self) {
self.clear()
}
}
#[cfg(feature = "std")]
impl<T: Eq + Hash> Collection for HashSet<T> {
type Item = T;
fn with_capacity(capacity: usize) -> Self {
Self::with_capacity(capacity)
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
self.insert(elem);
}
fn clear(&mut self) {
self.clear()
}
}
impl<T: Ord> Collection for BTreeSet<T> {
type Item = T;
#[doc(hidden)]
fn with_capacity(_capacity: usize) -> Self {
BTreeSet::new()
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
self.insert(elem);
}
fn clear(&mut self) {
self.clear()
}
}
#[cfg(feature = "std")]
impl<K: Eq + Hash, V> Collection for HashMap<K, V> {
type Item = (K, V);
fn with_capacity(capacity: usize) -> Self {
Self::with_capacity(capacity)
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
HashMap::insert(self, elem.0, elem.1);
}
fn clear(&mut self) {
self.clear()
}
}
#[cfg(feature = "std")]
impl<K: Eq + Hash, V> KeyedCollection for HashMap<K, V> {
type Key = K;
type Value = V;
fn get_mut(&mut self, key: &Self::Key) -> Option<&mut Self::Value> {
HashMap::get_mut(self, key)
}
fn insert(&mut self, key: Self::Key, value: Self::Value) -> Option<Self::Value> {
HashMap::insert(self, key, value)
}
fn remove(&mut self, key: &Self::Key) -> Option<Self::Value> {
HashMap::remove(self, key)
}
}
impl<K: Ord + Hash, V> Collection for BTreeMap<K, V> {
type Item = (K, V);
#[doc(hidden)]
fn with_capacity(_capacity: usize) -> Self {
BTreeMap::new()
}
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, elem: Self::Item) {
BTreeMap::insert(self, elem.0, elem.1);
}
fn clear(&mut self) {
self.clear()
}
}
impl<K: Ord + Hash, V> KeyedCollection for BTreeMap<K, V> {
type Key = K;
type Value = V;
fn get_mut(&mut self, key: &Self::Key) -> Option<&mut Self::Value> {
BTreeMap::get_mut(self, key)
}
fn insert(&mut self, key: Self::Key, value: Self::Value) -> Option<Self::Value> {
BTreeMap::insert(self, key, value)
}
fn remove(&mut self, key: &Self::Key) -> Option<Self::Value> {
BTreeMap::remove(self, key)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
Undersize {
len: usize,
min_len: usize,
},
Oversize {
len: usize,
max_len: usize,
},
OutOfBoundary {
index: usize,
len: usize,
},
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Error::Undersize { len, min_len } => write!(
f,
"operation results in collection size {len} less than lower boundary \
of {min_len}, which is prohibited"
),
Error::Oversize { len, max_len } => write!(
f,
"operation results in collection size {len} exceeding {max_len}, \
which is prohibited"
),
Error::OutOfBoundary { index, len } => write!(
f,
"attempt to access the element at {index} which is outside of the \
collection length boundary {len}"
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AsciiError {
Ascii(AsAsciiStrError),
Confinement(Error),
}
impl From<AsAsciiStrError> for AsciiError {
fn from(err: AsAsciiStrError) -> Self {
AsciiError::Ascii(err)
}
}
impl From<Error> for AsciiError {
fn from(err: Error) -> Self {
AsciiError::Confinement(err)
}
}
impl Display for AsciiError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
AsciiError::Ascii(e) => Display::fmt(e, f),
AsciiError::Confinement(e) => Display::fmt(e, f),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for AsciiError {}
pub const ZERO: usize = 0;
pub const ONE: usize = 1;
pub const U8: usize = u8::MAX as usize;
pub const U16: usize = u16::MAX as usize;
pub const U24: usize = 0xFFFFFFusize;
pub const U32: usize = u32::MAX as usize;
pub const U64: usize = u64::MAX as usize;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub struct Confined<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize>(C);
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Wrapper
for Confined<C, MIN_LEN, MAX_LEN>
{
type Inner = C;
fn from_inner(inner: Self::Inner) -> Self {
Self(inner)
}
fn as_inner(&self) -> &Self::Inner {
&self.0
}
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Deref
for Confined<C, MIN_LEN, MAX_LEN>
{
type Target = C;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<C, const MIN_LEN: usize, const MAX_LEN: usize> AsRef<[C::Item]>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Collection + AsRef<[C::Item]>,
{
fn as_ref(&self) -> &[C::Item] {
self.0.as_ref()
}
}
impl<C, const MIN_LEN: usize, const MAX_LEN: usize> AsMut<[C::Item]>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Collection + AsMut<[C::Item]>,
{
fn as_mut(&mut self) -> &mut [C::Item] {
self.0.as_mut()
}
}
impl<C, const MIN_LEN: usize, const MAX_LEN: usize> Borrow<[C::Item]>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Collection + Borrow<[C::Item]>,
{
fn borrow(&self) -> &[C::Item] {
self.0.borrow()
}
}
impl<C, const MIN_LEN: usize, const MAX_LEN: usize> BorrowMut<[C::Item]>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Collection + BorrowMut<[C::Item]>,
{
fn borrow_mut(&mut self) -> &mut [C::Item] {
self.0.borrow_mut()
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IntoIterator
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IntoIterator,
{
type Item = <C as IntoIterator>::Item;
type IntoIter = <C as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'c, C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IntoIterator
for &'c Confined<C, MIN_LEN, MAX_LEN>
where
&'c C: IntoIterator,
{
type Item = <&'c C as IntoIterator>::Item;
type IntoIter = <&'c C as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'c, C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IntoIterator
for &'c mut Confined<C, MIN_LEN, MAX_LEN>
where
&'c mut C: IntoIterator,
{
type Item = <&'c mut C as IntoIterator>::Item;
type IntoIter = <&'c mut C as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'c, C, const MIN_LEN: usize, const MAX_LEN: usize> Confined<C, MIN_LEN, MAX_LEN>
where
C: Collection + 'c,
&'c mut C: IntoIterator<Item = &'c mut <C as Collection>::Item>,
{
pub fn iter_mut(&'c mut self) -> <&'c mut C as IntoIterator>::IntoIter {
let coll = &mut self.0;
coll.into_iter()
}
}
impl<'c, C, const MIN_LEN: usize, const MAX_LEN: usize> Confined<C, MIN_LEN, MAX_LEN>
where
C: KeyedCollection + 'c,
&'c mut C: IntoIterator<
Item = (
&'c <C as KeyedCollection>::Key,
&'c mut <C as KeyedCollection>::Value,
),
>,
{
pub fn keyed_values_mut(&'c mut self) -> <&'c mut C as IntoIterator>::IntoIter {
let coll = &mut self.0;
coll.into_iter()
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Index<usize>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Index<usize, Output = C::Item>,
{
type Output = C::Item;
fn index(&self, index: usize) -> &Self::Output {
self.0.index(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IndexMut<usize>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IndexMut<usize, Output = C::Item>,
{
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Index<Range<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Index<Range<usize>, Output = [C::Item]>,
{
type Output = [C::Item];
fn index(&self, index: Range<usize>) -> &Self::Output {
self.0.index(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IndexMut<Range<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IndexMut<Range<usize>, Output = [C::Item]>,
{
fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Index<RangeTo<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Index<RangeTo<usize>, Output = [C::Item]>,
{
type Output = [C::Item];
fn index(&self, index: RangeTo<usize>) -> &Self::Output {
self.0.index(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IndexMut<RangeTo<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IndexMut<RangeTo<usize>, Output = [C::Item]>,
{
fn index_mut(&mut self, index: RangeTo<usize>) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Index<RangeFrom<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Index<RangeFrom<usize>, Output = [C::Item]>,
{
type Output = [C::Item];
fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
self.0.index(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IndexMut<RangeFrom<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IndexMut<RangeFrom<usize>, Output = [C::Item]>,
{
fn index_mut(&mut self, index: RangeFrom<usize>) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Index<RangeInclusive<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Index<RangeInclusive<usize>, Output = [C::Item]>,
{
type Output = [C::Item];
fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
self.0.index(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IndexMut<RangeInclusive<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IndexMut<RangeInclusive<usize>, Output = [C::Item]>,
{
fn index_mut(&mut self, index: RangeInclusive<usize>) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Index<RangeToInclusive<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Index<RangeToInclusive<usize>, Output = [C::Item]>,
{
type Output = [C::Item];
fn index(&self, index: RangeToInclusive<usize>) -> &Self::Output {
self.0.index(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IndexMut<RangeToInclusive<usize>>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IndexMut<RangeToInclusive<usize>, Output = [C::Item]>,
{
fn index_mut(&mut self, index: RangeToInclusive<usize>) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Index<RangeFull>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Index<RangeFull, Output = [C::Item]>,
{
type Output = [C::Item];
fn index(&self, index: RangeFull) -> &Self::Output {
self.0.index(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> IndexMut<RangeFull>
for Confined<C, MIN_LEN, MAX_LEN>
where
C: IndexMut<RangeFull, Output = [C::Item]>,
{
fn index_mut(&mut self, index: RangeFull) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Display
for Confined<C, MIN_LEN, MAX_LEN>
where
C: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> FromStr
for Confined<C, MIN_LEN, MAX_LEN>
where
C: FromStr,
{
type Err = C::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
C::from_str(s).map(Self)
}
}
impl<C: Collection, const MIN_LEN: usize, const MAX_LEN: usize> Confined<C, MIN_LEN, MAX_LEN> {
pub fn from_collection_unsafe(col: C) -> Self {
Self::try_from(col).expect("collection size mismatch, use try_from instead")
}
pub fn try_from(col: C) -> Result<Self, Error> {
let len = col.len();
if len < MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
if len > MAX_LEN {
return Err(Error::Oversize {
len,
max_len: MAX_LEN,
});
}
Ok(Self(col))
}
pub fn try_from_iter<I: IntoIterator<Item = C::Item>>(iter: I) -> Result<Self, Error> {
let mut col = C::with_capacity(MIN_LEN);
for item in iter {
col.push(item);
}
Self::try_from(col)
}
pub fn from_iter_unsafe<I: IntoIterator<Item = C::Item>>(iter: I) -> Self {
let mut col = C::with_capacity(MIN_LEN);
for item in iter {
col.push(item);
}
Self::from_collection_unsafe(col)
}
pub fn as_inner(&self) -> &C {
&self.0
}
pub fn to_inner(&self) -> C
where
C: Clone,
{
self.0.clone()
}
pub fn into_inner(self) -> C {
self.0
}
pub fn push(&mut self, elem: C::Item) -> Result<(), Error> {
let len = self.len();
if len == MAX_LEN || len + 1 > MAX_LEN {
return Err(Error::Oversize {
len: len + 1,
max_len: MAX_LEN,
});
}
self.0.push(elem);
Ok(())
}
pub fn extend<T: IntoIterator<Item = C::Item>>(&mut self, iter: T) -> Result<(), Error> {
for elem in iter {
self.push(elem)?;
}
Ok(())
}
pub fn unbox(self) -> C {
self.0
}
}
impl<C: Collection, const MAX_LEN: usize> Confined<C, ZERO, MAX_LEN>
where
C: Default,
{
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self(C::with_capacity(capacity))
}
pub fn clear(&mut self) {
self.0.clear()
}
}
impl<C: Collection, const MAX_LEN: usize> Default for Confined<C, ZERO, MAX_LEN>
where
C: Default,
{
fn default() -> Self {
Self(C::default())
}
}
impl<C: Collection, const MAX_LEN: usize> Confined<C, ONE, MAX_LEN>
where
C: Default,
{
pub fn with(elem: C::Item) -> Self {
let mut c = C::default();
c.push(elem);
Self(c)
}
}
impl<C: Collection, const MIN_LEN: usize> Confined<C, MIN_LEN, U8>
where
C: Default,
{
pub fn len_u8(&self) -> u8 {
self.len() as u8
}
}
impl<C: Collection, const MIN_LEN: usize> Confined<C, MIN_LEN, U16>
where
C: Default,
{
pub fn len_u16(&self) -> u16 {
self.len() as u16
}
}
impl<C: Collection, const MIN_LEN: usize> Confined<C, MIN_LEN, U24>
where
C: Default,
{
pub fn len_u24(&self) -> u24 {
u24::try_from(self.len() as u32).expect("confinement broken")
}
}
impl<C: Collection, const MIN_LEN: usize> Confined<C, MIN_LEN, U32>
where
C: Default,
{
pub fn len_u32(&self) -> u32 {
self.len() as u32
}
}
impl<C: KeyedCollection, const MIN_LEN: usize, const MAX_LEN: usize> Confined<C, MIN_LEN, MAX_LEN> {
pub fn get_mut(&mut self, key: &C::Key) -> Option<&mut C::Value> {
self.0.get_mut(key)
}
pub fn insert(&mut self, key: C::Key, value: C::Value) -> Result<Option<C::Value>, Error> {
let len = self.len();
if len == MAX_LEN || len + 1 > MAX_LEN {
return Err(Error::Oversize {
len: len + 1,
max_len: MAX_LEN,
});
}
Ok(self.0.insert(key, value))
}
}
impl<C: KeyedCollection, const MAX_LEN: usize> Confined<C, ONE, MAX_LEN>
where
C: Default,
{
pub fn with_key_value(key: C::Key, value: C::Value) -> Self {
let mut c = C::default();
c.insert(key, value);
Self(c)
}
}
impl<const MIN_LEN: usize, const MAX_LEN: usize> TryFrom<&str>
for Confined<String, MIN_LEN, MAX_LEN>
{
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::try_from(value.to_owned())
}
}
impl<const MIN_LEN: usize, const MAX_LEN: usize> TryFrom<&str>
for Confined<AsciiString, MIN_LEN, MAX_LEN>
{
type Error = AsciiError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let a = AsciiString::from_str(value)?;
Self::try_from(a).map_err(AsciiError::from)
}
}
impl<const MAX_LEN: usize> Confined<String, ZERO, MAX_LEN> {
pub fn pop(&mut self) -> Option<char> {
self.0.pop()
}
}
impl<const MIN_LEN: usize, const MAX_LEN: usize> Confined<String, MIN_LEN, MAX_LEN> {
pub fn remove(&mut self, index: usize) -> Result<char, Error> {
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
if index >= len {
return Err(Error::OutOfBoundary { index, len });
}
Ok(self.0.remove(index))
}
}
impl<const MAX_LEN: usize> Confined<AsciiString, ZERO, MAX_LEN> {
pub fn pop(&mut self) -> Option<AsciiChar> {
self.0.pop()
}
}
impl<const MIN_LEN: usize, const MAX_LEN: usize> Confined<AsciiString, MIN_LEN, MAX_LEN> {
pub fn remove(&mut self, index: usize) -> Result<AsciiChar, Error> {
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
if index >= len {
return Err(Error::OutOfBoundary { index, len });
}
Ok(self.0.remove(index))
}
}
impl<T, const MIN_LEN: usize, const MAX_LEN: usize> Confined<Vec<T>, MIN_LEN, MAX_LEN> {
#[inline]
pub fn from_slice_unsafe(slice: &[T]) -> Self
where
T: Clone,
{
assert!(slice.len() > MIN_LEN && slice.len() <= MAX_LEN);
Self(slice.to_vec())
}
#[inline]
pub fn try_from_slice(slice: &[T]) -> Result<Self, Error>
where
T: Clone,
{
Self::try_from(slice.to_vec())
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.0
}
#[inline]
pub fn into_vec(self) -> Vec<T> {
self.0
}
#[inline]
pub fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
where
I: SliceIndex<[T]>,
{
self.0.get_mut(index)
}
}
impl<T, const MAX_LEN: usize> Confined<Vec<T>, ZERO, MAX_LEN> {
#[inline]
pub fn pop(&mut self) -> Option<T> {
self.0.pop()
}
}
impl<T, const MIN_LEN: usize, const MAX_LEN: usize> Confined<Vec<T>, MIN_LEN, MAX_LEN> {
pub fn remove(&mut self, index: usize) -> Result<T, Error> {
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
if index >= len {
return Err(Error::OutOfBoundary { index, len });
}
Ok(self.0.remove(index))
}
pub fn iter(&self) -> core::slice::Iter<T> {
self.0.iter()
}
}
impl<T, const MIN_LEN: usize, const MAX_LEN: usize> Confined<VecDeque<T>, MIN_LEN, MAX_LEN> {
pub fn pop_front(&mut self) -> Option<T> {
self.0.pop_front()
}
pub fn pop_back(&mut self) -> Option<T> {
self.0.pop_back()
}
}
impl<T, const MIN_LEN: usize, const MAX_LEN: usize> Confined<VecDeque<T>, MIN_LEN, MAX_LEN> {
pub fn push_from(&mut self, elem: T) -> Result<(), Error> {
let len = self.len();
if len == MAX_LEN || len + 1 > MAX_LEN {
return Err(Error::Oversize {
len: len + 1,
max_len: MAX_LEN,
});
}
self.0.push_front(elem);
Ok(())
}
pub fn push_back(&mut self, elem: T) -> Result<(), Error> {
let len = self.len();
if len == MAX_LEN || len + 1 > MAX_LEN {
return Err(Error::Oversize {
len: len + 1,
max_len: MAX_LEN,
});
}
self.0.push_back(elem);
Ok(())
}
pub fn remove(&mut self, index: usize) -> Result<T, Error> {
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
if index >= len {
return Err(Error::OutOfBoundary { index, len });
}
Ok(self.0.remove(index).expect("element within the length"))
}
}
#[cfg(feature = "std")]
impl<T: Hash + Eq, const MIN_LEN: usize, const MAX_LEN: usize>
Confined<HashSet<T>, MIN_LEN, MAX_LEN>
{
pub fn remove(&mut self, elem: &T) -> Result<bool, Error> {
if !self.0.contains(elem) {
return Ok(false);
}
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
Ok(self.0.remove(elem))
}
pub fn take(&mut self, elem: &T) -> Result<Option<T>, Error> {
if !self.0.contains(elem) {
return Ok(None);
}
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
Ok(self.0.take(elem))
}
}
impl<T: Ord, const MIN_LEN: usize, const MAX_LEN: usize> Confined<BTreeSet<T>, MIN_LEN, MAX_LEN> {
pub fn remove(&mut self, elem: &T) -> Result<bool, Error> {
if !self.0.contains(elem) {
return Ok(false);
}
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
Ok(self.0.remove(elem))
}
pub fn take(&mut self, elem: &T) -> Result<Option<T>, Error> {
if !self.0.contains(elem) {
return Ok(None);
}
let len = self.len();
if self.is_empty() || len - 1 <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
Ok(self.0.take(elem))
}
}
#[cfg(feature = "std")]
impl<K: Hash + Eq, V, const MIN_LEN: usize, const MAX_LEN: usize>
Confined<HashMap<K, V>, MIN_LEN, MAX_LEN>
{
pub fn remove(&mut self, key: &K) -> Result<Option<V>, Error> {
if !self.0.contains_key(key) {
return Ok(None);
}
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
Ok(self.0.remove(key))
}
pub fn into_keys(self) -> hash_map::IntoKeys<K, V> {
self.0.into_keys()
}
pub fn into_values(self) -> hash_map::IntoValues<K, V> {
self.0.into_values()
}
}
impl<K: Ord + Hash, V, const MIN_LEN: usize, const MAX_LEN: usize>
Confined<BTreeMap<K, V>, MIN_LEN, MAX_LEN>
{
pub fn remove(&mut self, key: &K) -> Result<Option<V>, Error> {
if !self.0.contains_key(key) {
return Ok(None);
}
let len = self.len();
if self.is_empty() || len <= MIN_LEN {
return Err(Error::Undersize {
len,
min_len: MIN_LEN,
});
}
Ok(self.0.remove(key))
}
pub fn into_keys(self) -> btree_map::IntoKeys<K, V> {
self.0.into_keys()
}
pub fn into_values(self) -> btree_map::IntoValues<K, V> {
self.0.into_values()
}
}
#[cfg(feature = "std")]
impl<const MAX_LEN: usize> io::Write for Confined<Vec<u8>, ZERO, MAX_LEN> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if buf.len() + self.len() >= MAX_LEN {
return Err(io::Error::from(io::ErrorKind::OutOfMemory));
}
self.0.extend(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<const MIN_LEN: usize, const MAX_LEN: usize> LowerHex for Confined<Vec<u8>, MIN_LEN, MAX_LEN> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.0.to_hex())
}
}
impl<const MIN_LEN: usize, const MAX_LEN: usize> UpperHex for Confined<Vec<u8>, MIN_LEN, MAX_LEN> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.0.to_hex().to_uppercase())
}
}
impl<const MIN_LEN: usize, const MAX_LEN: usize> FromHex for Confined<Vec<u8>, MIN_LEN, MAX_LEN> {
fn from_byte_iter<I>(iter: I) -> Result<Self, hex::Error>
where
I: Iterator<Item = Result<u8, hex::Error>> + ExactSizeIterator + DoubleEndedIterator,
{
Vec::<u8>::from_byte_iter(iter).map(Self)
}
}
pub type TinyString = Confined<String, ZERO, U8>;
pub type SmallString = Confined<String, ZERO, U16>;
pub type MediumString = Confined<String, ZERO, U24>;
pub type LargeString = Confined<String, ZERO, U32>;
pub type NonEmptyString<const MAX: usize = U64> = Confined<String, ONE, MAX>;
pub type TinyAscii = Confined<AsciiString, ZERO, U8>;
pub type SmallAscii = Confined<AsciiString, ZERO, U16>;
pub type MediumAscii = Confined<AsciiString, ZERO, U24>;
pub type LargeAscii = Confined<AsciiString, ZERO, U32>;
pub type NonEmptyAscii<const MAX: usize = U64> = Confined<AsciiString, ONE, MAX>;
pub type TinyBlob = Confined<Vec<u8>, ZERO, U8>;
pub type SmallBlob = Confined<Vec<u8>, ZERO, U16>;
pub type MediumBlob = Confined<Vec<u8>, ZERO, U24>;
pub type LargeBlob = Confined<Vec<u8>, ZERO, U32>;
pub type NonEmptyBlob<const MAX: usize = U64> = Confined<Vec<u8>, ONE, MAX>;
pub type TinyVec<T> = Confined<Vec<T>, ZERO, U8>;
pub type SmallVec<T> = Confined<Vec<T>, ZERO, U16>;
pub type MediumVec<T> = Confined<Vec<T>, ZERO, U24>;
pub type LargeVec<T> = Confined<Vec<T>, ZERO, U32>;
pub type NonEmptyVec<T, const MAX: usize = U64> = Confined<Vec<T>, ONE, MAX>;
pub type TinyDeque<T> = Confined<VecDeque<T>, ZERO, U8>;
pub type SmallDeque<T> = Confined<VecDeque<T>, ZERO, U16>;
pub type MediumDeque<T> = Confined<VecDeque<T>, ZERO, U24>;
pub type LargeDeque<T> = Confined<VecDeque<T>, ZERO, U32>;
pub type NonEmptyDeque<T, const MAX: usize = U64> = Confined<VecDeque<T>, ONE, MAX>;
#[cfg(feature = "std")]
pub type TinyHashSet<T> = Confined<HashSet<T>, ZERO, U8>;
#[cfg(feature = "std")]
pub type SmallHashSet<T> = Confined<HashSet<T>, ZERO, U16>;
#[cfg(feature = "std")]
pub type MediumHashSet<T> = Confined<HashSet<T>, ZERO, U24>;
#[cfg(feature = "std")]
pub type LargeHashSet<T> = Confined<HashSet<T>, ZERO, U32>;
#[cfg(feature = "std")]
pub type NonEmptyHashSet<T, const MAX: usize = U64> = Confined<HashSet<T>, ONE, MAX>;
pub type TinyOrdSet<T> = Confined<BTreeSet<T>, ZERO, U8>;
pub type SmallOrdSet<T> = Confined<BTreeSet<T>, ZERO, U16>;
pub type MediumOrdSet<T> = Confined<BTreeSet<T>, ZERO, U24>;
pub type LargeOrdSet<T> = Confined<BTreeSet<T>, ZERO, U32>;
pub type NonEmptyOrdSet<T, const MAX: usize = U64> = Confined<BTreeSet<T>, ONE, MAX>;
#[cfg(feature = "std")]
pub type TinyHashMap<K, V> = Confined<HashMap<K, V>, ZERO, U8>;
#[cfg(feature = "std")]
pub type SmallHashMap<K, V> = Confined<HashMap<K, V>, ZERO, U16>;
#[cfg(feature = "std")]
pub type MediumHashMap<K, V> = Confined<HashMap<K, V>, ZERO, U24>;
#[cfg(feature = "std")]
pub type LargeHashMap<K, V> = Confined<HashMap<K, V>, ZERO, U32>;
#[cfg(feature = "std")]
pub type NonEmptyHashMap<K, V, const MAX: usize = U64> = Confined<HashMap<K, V>, ONE, MAX>;
pub type TinyOrdMap<K, V> = Confined<BTreeMap<K, V>, ZERO, U8>;
pub type SmallOrdMap<K, V> = Confined<BTreeMap<K, V>, ZERO, U16>;
pub type MediumOrdMap<K, V> = Confined<BTreeMap<K, V>, ZERO, U24>;
pub type LargeOrdMap<K, V> = Confined<BTreeMap<K, V>, ZERO, U32>;
pub type NonEmptyOrdMap<K, V, const MAX: usize = U64> = Confined<BTreeMap<K, V>, ONE, MAX>;
#[macro_export]
macro_rules! tiny_s {
($lit:literal) => {
$crate::confinement::TinyString::try_from(s!($lit))
.expect("static string for tiny_s literal cis too long")
};
}
#[macro_export]
macro_rules! small_s {
($lit:literal) => {
$crate::confinement::SmallString::try_from(s!($lit))
.expect("static string for small_s literal cis too long")
};
}
#[macro_export]
macro_rules! confined_vec {
($elem:expr; $n:expr) => (
$crate::confinement::Confined::try_from(vec![$elem; $n])
.expect("inline confined_vec literal contains invalid number of items")
);
($($x:expr),+ $(,)?) => (
$crate::confinement::Confined::try_from(vec![$($x,)+])
.expect("inline confined_vec literal contains invalid number of items")
.into()
)
}
#[macro_export]
macro_rules! tiny_vec {
($elem:expr; $n:expr) => (
$crate::confinement::TinyVec::try_from(vec![$elem; $n])
.expect("inline tiny_vec literal contains invalid number of items")
);
($($x:expr),+ $(,)?) => (
$crate::confinement::TinyVec::try_from(vec![$($x,)+])
.expect("inline tiny_vec literal contains invalid number of items")
)
}
#[macro_export]
macro_rules! small_vec {
($elem:expr; $n:expr) => (
$crate::confinement::SmallVec::try_from(vec![$elem; $n])
.expect("inline small_vec literal contains invalid number of items")
);
($($x:expr),+ $(,)?) => (
$crate::confinement::SmallVec::try_from(vec![$($x,)+])
.expect("inline small_vec literal contains invalid number of items")
)
}
#[macro_export]
macro_rules! confined_set {
($($x:expr),+ $(,)?) => (
$crate::confinement::Confined::try_from(set![$($x,)+])
.expect("inline confined_set literal contains invalid number of items")
.into()
)
}
#[macro_export]
macro_rules! tiny_set {
($($x:expr),+ $(,)?) => (
$crate::confinement::TinyHashSet::try_from(set![$($x,)+])
.expect("inline tiny_set literal contains invalid number of items")
)
}
#[macro_export]
macro_rules! small_set {
($($x:expr),+ $(,)?) => (
$crate::confinement::SmallHashSet::try_from(set![$($x,)+])
.expect("inline small_set literal contains invalid number of items")
)
}
#[macro_export]
macro_rules! confined_bset {
($($x:expr),+ $(,)?) => (
$crate::confinement::Confined::try_from(bset![$($x,)+])
.expect("inline confined_bset literal contains invalid number of items")
.into()
)
}
#[macro_export]
macro_rules! tiny_bset {
($($x:expr),+ $(,)?) => (
$crate::confinement::TinyOrdSet::try_from(bset![$($x,)+])
.expect("inline tiny_bset literal contains invalid number of items")
)
}
#[macro_export]
macro_rules! small_bset {
($($x:expr),+ $(,)?) => (
$crate::confinement::SmallOrdSet::try_from(bset![$($x,)+])
.expect("inline small_bset literal contains invalid number of items")
)
}
#[macro_export]
macro_rules! confined_map {
($($key:expr => $value:expr),+ $(,)?) => (
$crate::confinement::Confined::try_from(map!{ $($key => $value),+ })
.expect("inline confined_map literal contains invalid number of items")
.into()
)
}
#[macro_export]
macro_rules! tiny_map {
{ $($key:expr => $value:expr),+ $(,)? } => {
$crate::confinement::TinyHashMap::try_from(map!{ $($key => $value,)+ })
.expect("inline tiny_map literal contains invalid number of items")
}
}
#[macro_export]
macro_rules! small_map {
{ $($key:expr => $value:expr),+ $(,)? } => {
$crate::confinement::SmallHashMap::try_from(map!{ $($key => $value,)+ })
.expect("inline small_map literal contains invalid number of items")
}
}
#[macro_export]
macro_rules! confined_bmap {
($($key:expr => $value:expr),+ $(,)?) => (
$crate::confinement::Confined::try_from(bmap!{ $($key => $value),+ })
.expect("inline confined_bmap literal contains invalid number of items")
.into()
)
}
#[macro_export]
macro_rules! tiny_bmap {
{ $($key:expr => $value:expr),+ $(,)? } => {
$crate::confinement::TinyOrdMap::try_from(bmap!{ $($key => $value,)+ })
.expect("inline tiny_bmap literal contains invalid number of items")
}
}
#[macro_export]
macro_rules! small_bmap {
{ $($key:expr => $value:expr),+ $(,)? } => {
$crate::confinement::SmallOrdMap::try_from(bmap!{ $($key => $value,)+ })
.expect("inline small_bmap literal contains invalid number of items")
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn fits_max() {
let mut s = TinyString::new();
assert!(s.is_empty());
for _ in 1..=255 {
s.push('a').unwrap();
}
assert_eq!(s.len_u8(), u8::MAX);
assert_eq!(s.len_u8(), s.len() as u8);
assert!(!s.is_empty());
let mut vec = TinyVec::new();
let mut deque = TinyDeque::new();
let mut set = TinyHashSet::new();
let mut bset = TinyOrdSet::new();
let mut map = TinyHashMap::new();
let mut bmap = TinyOrdMap::new();
assert!(vec.is_empty());
assert!(deque.is_empty());
assert!(set.is_empty());
assert!(bset.is_empty());
assert!(map.is_empty());
assert!(bmap.is_empty());
for index in 1..=255 {
vec.push(5u8).unwrap();
deque.push(5u8).unwrap();
set.push(index).unwrap();
bset.push(5u8).unwrap();
map.insert(5u8, 'a').unwrap();
bmap.insert(index, 'a').unwrap();
}
assert_eq!(vec.len_u8(), u8::MAX);
assert_eq!(deque.len_u8(), u8::MAX);
assert_eq!(set.len_u8(), u8::MAX);
assert_eq!(bset.len_u8(), 1);
assert_eq!(map.len_u8(), 1);
assert_eq!(bmap.len_u8(), u8::MAX);
vec.clear();
assert!(vec.is_empty());
}
#[test]
#[should_panic(expected = "Oversize")]
fn cant_go_above_max() {
let mut s = TinyString::new();
for _ in 1..=256 {
s.push('a').unwrap();
}
}
#[test]
#[should_panic(expected = "Undersize")]
fn cant_go_below_min() {
let mut s = NonEmptyString::<U8>::with('a');
s.remove(0).unwrap();
}
#[test]
fn macros() {
tiny_vec!("a", "b", "c");
small_vec!("a", "b", "c");
tiny_set!("a", "b", "c");
tiny_bset!("a", "b", "c");
tiny_map!("a" => 1, "b" => 2, "c" => 3);
tiny_bmap!("a" => 1, "b" => 2, "c" => 3);
small_set!("a", "b", "c");
small_bset!("a", "b", "c");
small_map!("a" => 1, "b" => 2, "c" => 3);
small_bmap!("a" => 1, "b" => 2, "c" => 3);
let _: TinyHashMap<_, _> = confined_map!("a" => 1, "b" => 2, "c" => 3);
}
}