mod impls;
mod iter;
use std::{
fmt,
ops::{Bound, Range, RangeBounds},
};
use borsh::{BorshDeserialize, BorshSerialize};
use unc_sdk_macros::UncSchema;
pub use self::iter::{Drain, Iter, IterMut};
use super::ERR_INCONSISTENT_STATE;
use crate::{env, IntoStorageKey};
use super::IndexMap;
const ERR_INDEX_OUT_OF_BOUNDS: &str = "Index out of bounds";
fn expect_consistent_state<T>(val: Option<T>) -> T {
val.unwrap_or_else(|| env::panic_str(ERR_INCONSISTENT_STATE))
}
#[derive(UncSchema, BorshSerialize, BorshDeserialize)]
#[inside_uncsdk]
#[abi(borsh)]
pub struct Vector<T>
where
T: BorshSerialize,
{
pub(crate) len: u32,
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
pub(crate) values: IndexMap<T>,
}
#[test]
fn collections_vec_not_backwards_compatible() {
use crate::collections::Vector as Vec1;
let mut v1 = Vec1::new(b"m");
v1.extend([1u8, 2, 3, 4]);
assert!(Vector::<u8>::try_from_slice(&borsh::to_vec(&v1).unwrap()).is_err());
}
impl<T> Vector<T>
where
T: BorshSerialize,
{
pub fn len(&self) -> u32 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn new<S>(prefix: S) -> Self
where
S: IntoStorageKey,
{
Self { len: 0, values: IndexMap::new(prefix) }
}
pub fn clear(&mut self) {
for i in 0..self.len {
self.values.set(i, None);
}
self.len = 0;
}
pub fn flush(&mut self) {
self.values.flush();
}
pub fn set(&mut self, index: u32, value: T) {
if index >= self.len() {
env::panic_str(ERR_INDEX_OUT_OF_BOUNDS);
}
self.values.set(index, Some(value));
}
pub fn push(&mut self, element: T) {
let last_idx = self.len();
self.len =
self.len.checked_add(1).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS));
self.set(last_idx, element)
}
}
impl<T> Vector<T>
where
T: BorshSerialize + BorshDeserialize,
{
pub fn get(&self, index: u32) -> Option<&T> {
if index >= self.len() {
return None;
}
self.values.get(index)
}
pub fn get_mut(&mut self, index: u32) -> Option<&mut T> {
if index >= self.len {
return None;
}
self.values.get_mut(index)
}
pub(crate) fn swap(&mut self, a: u32, b: u32) {
if a >= self.len() || b >= self.len() {
env::panic_str(ERR_INDEX_OUT_OF_BOUNDS);
}
self.values.swap(a, b);
}
pub fn swap_remove(&mut self, index: u32) -> T {
if self.is_empty() {
env::panic_str(ERR_INDEX_OUT_OF_BOUNDS);
}
self.swap(index, self.len() - 1);
expect_consistent_state(self.pop())
}
pub fn pop(&mut self) -> Option<T> {
let new_idx = self.len.checked_sub(1)?;
let prev = self.values.remove(new_idx);
self.len = new_idx;
prev
}
pub fn replace(&mut self, index: u32, element: T) -> T {
if index >= self.len {
env::panic_str(ERR_INDEX_OUT_OF_BOUNDS);
}
self.values.insert(index, element).unwrap()
}
pub fn iter(&self) -> Iter<T> {
Iter::new(self)
}
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut::new(self)
}
pub fn drain<R>(&mut self, range: R) -> Drain<T>
where
R: RangeBounds<u32>,
{
let start = match range.start_bound() {
Bound::Excluded(i) => {
i.checked_add(1).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS))
}
Bound::Included(i) => *i,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Excluded(i) => *i,
Bound::Included(i) => {
i.checked_add(1).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS))
}
Bound::Unbounded => self.len(),
};
Drain::new(self, Range { start, end: core::cmp::min(end, self.len()) })
}
}
impl<T> fmt::Debug for Vector<T>
where
T: BorshSerialize + BorshDeserialize + fmt::Debug,
{
#[cfg(feature = "expensive-debug")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.iter().collect::<Vec<_>>(), f)
}
#[cfg(not(feature = "expensive-debug"))]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Vector")
.field("len", &self.len)
.field("prefix", &self.values.prefix)
.finish()
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests {
use arbitrary::{Arbitrary, Unstructured};
use borsh::{to_vec, BorshDeserialize};
use rand::{Rng, RngCore, SeedableRng};
use std::ops::{Bound, IndexMut};
use super::Vector;
use crate::{store::IndexMap, test_utils::test_env::setup_free};
#[test]
fn test_push_pop() {
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
let mut vec = Vector::new(b"v".to_vec());
let mut baseline = vec![];
for _ in 0..500 {
let value = rng.gen::<u64>();
vec.push(value);
baseline.push(value);
}
let actual: Vec<u64> = vec.iter().cloned().collect();
assert_eq!(actual, baseline);
for _ in 0..501 {
assert_eq!(baseline.pop(), vec.pop());
}
}
#[test]
#[should_panic]
fn test_set_panic() {
let mut vec = Vector::new(b"b");
vec.set(2, 0);
}
#[test]
fn test_get_mut_none() {
let mut vec: Vector<bool> = Vector::new(b"b");
assert!(vec.get_mut(2).is_none());
}
#[test]
#[should_panic]
fn test_drain_panic() {
let mut vec: Vector<bool> = Vector::new(b"b");
vec.drain(..=u32::MAX);
}
#[test]
#[should_panic]
fn test_drain_panic_2() {
let mut vec: Vector<bool> = Vector::new(b"b");
vec.drain((Bound::Excluded(u32::MAX), Bound::Included(u32::MAX)));
}
#[test]
fn test_replace_method() {
let mut vec = Vector::new(b"b");
vec.push(10);
vec.replace(0, 2);
assert_eq!(vec[0], 2);
}
#[test]
#[should_panic]
fn test_replace_method_panic() {
let mut vec = Vector::new(b"b");
vec.replace(0, 2);
}
#[test]
pub fn test_replace() {
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(1);
let mut vec = Vector::new(b"v".to_vec());
let mut baseline = vec![];
for _ in 0..500 {
let value = rng.gen::<u64>();
vec.push(value);
baseline.push(value);
}
for _ in 0..500 {
let index = rng.gen::<u32>() % vec.len();
let value = rng.gen::<u64>();
let old_value0 = vec[index];
let old_value1 = core::mem::replace(vec.get_mut(index).unwrap(), value);
let old_value2 = baseline[index as usize];
assert_eq!(old_value0, old_value1);
assert_eq!(old_value0, old_value2);
*baseline.get_mut(index as usize).unwrap() = value;
}
let actual: Vec<_> = vec.iter().cloned().collect();
assert_eq!(actual, baseline);
}
#[test]
pub fn test_swap_remove() {
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(2);
let mut vec = Vector::new(b"v".to_vec());
let mut baseline = vec![];
for _ in 0..500 {
let value = rng.gen::<u64>();
vec.push(value);
baseline.push(value);
}
for _ in 0..500 {
let index = rng.gen::<u32>() % vec.len();
let old_value0 = vec[index];
let old_value1 = vec.swap_remove(index);
let old_value2 = baseline[index as usize];
let last_index = baseline.len() - 1;
baseline.swap(index as usize, last_index);
baseline.pop();
assert_eq!(old_value0, old_value1);
assert_eq!(old_value0, old_value2);
}
let actual: Vec<_> = vec.iter().cloned().collect();
assert_eq!(actual, baseline);
}
#[test]
#[should_panic]
pub fn test_swap_remove_panic() {
let mut vec: Vector<bool> = Vector::new(b"v".to_vec());
vec.swap_remove(1);
}
#[test]
#[should_panic]
pub fn test_swap_panic() {
let mut vec: Vector<bool> = Vector::new(b"v".to_vec());
vec.swap(1, 2);
}
#[test]
pub fn test_clear() {
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(3);
let mut vec = Vector::new(b"v".to_vec());
for _ in 0..100 {
for _ in 0..(rng.gen::<u64>() % 20 + 1) {
let value = rng.gen::<u64>();
vec.push(value);
}
assert!(!vec.is_empty());
vec.clear();
assert!(vec.is_empty());
}
}
#[test]
pub fn test_extend() {
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
let mut vec = Vector::new(b"v".to_vec());
let mut baseline = vec![];
for _ in 0..100 {
let value = rng.gen::<u64>();
vec.push(value);
baseline.push(value);
}
for _ in 0..100 {
let mut tmp = vec![];
for _ in 0..=(rng.gen::<u64>() % 20 + 1) {
let value = rng.gen::<u64>();
tmp.push(value);
}
baseline.extend(tmp.clone());
vec.extend(tmp.clone());
}
let actual: Vec<_> = vec.iter().cloned().collect();
assert_eq!(actual, baseline);
}
#[test]
fn test_debug() {
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(4);
let prefix = b"v".to_vec();
let mut vec = Vector::new(prefix.clone());
let mut baseline = vec![];
for _ in 0..10 {
let value = rng.gen::<u64>();
vec.push(value);
baseline.push(value);
}
let actual: Vec<_> = vec.iter().cloned().collect();
assert_eq!(actual, baseline);
for _ in 0..5 {
assert_eq!(baseline.pop(), vec.pop());
}
if cfg!(feature = "expensive-debug") {
assert_eq!(format!("{:#?}", vec), format!("{:#?}", baseline));
} else {
assert_eq!(
format!("{:?}", vec),
format!("Vector {{ len: 5, prefix: {:?} }}", vec.values.prefix)
);
}
vec.flush();
use unc_sdk_macros::unc;
#[unc(inside_uncsdk)]
#[derive(Debug)]
struct TestType(u64);
let deserialize_only_vec =
Vector::<TestType> { len: vec.len(), values: IndexMap::new(prefix) };
let baseline: Vec<_> = baseline.into_iter().map(TestType).collect();
if cfg!(feature = "expensive-debug") {
assert_eq!(format!("{:#?}", deserialize_only_vec), format!("{:#?}", baseline));
} else {
assert_eq!(
format!("{:?}", deserialize_only_vec),
format!("Vector {{ len: 5, prefix: {:?} }}", deserialize_only_vec.values.prefix)
);
}
}
#[test]
pub fn iterator_checks() {
let mut vec = Vector::new(b"v");
let mut baseline = vec![];
for i in 0..10 {
vec.push(i);
baseline.push(i);
}
let mut vec_iter = vec.iter();
let mut bl_iter = baseline.iter();
assert_eq!(vec_iter.next(), bl_iter.next());
assert_eq!(vec_iter.next_back(), bl_iter.next_back());
assert_eq!(vec_iter.nth(3), bl_iter.nth(3));
assert_eq!(vec_iter.nth_back(2), bl_iter.nth_back(2));
assert!(vec_iter.nth(5).is_none());
assert!(bl_iter.nth(5).is_none());
assert!(vec_iter.next().is_none());
assert!(bl_iter.next().is_none());
assert_eq!(vec.iter().count(), baseline.len());
}
#[test]
pub fn iterator_mut_checks() {
let mut vec = Vector::new(b"v");
let mut baseline = vec![];
for i in 0..10 {
vec.push(i);
baseline.push(i);
}
let mut vec_iter = vec.iter_mut();
let mut bl_iter = baseline.iter_mut();
assert_eq!(vec_iter.next(), bl_iter.next());
assert_eq!(vec_iter.next_back(), bl_iter.next_back());
assert_eq!(vec_iter.nth(3), bl_iter.nth(3));
assert_eq!(vec_iter.nth_back(2), bl_iter.nth_back(2));
assert!(vec_iter.nth(5).is_none());
assert!(bl_iter.nth(5).is_none());
assert!(vec_iter.next().is_none());
assert!(bl_iter.next().is_none());
assert_eq!(vec.iter().count(), baseline.len());
}
#[test]
fn drain_iterator() {
let mut vec = Vector::new(b"v");
let mut baseline = vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
vec.extend(baseline.clone());
assert!(Iterator::eq(vec.drain(1..=3), baseline.drain(1..=3)));
assert_eq!(vec.iter().copied().collect::<Vec<_>>(), vec![0, 4, 5, 6, 7, 8, 9]);
{
let mut drain = vec.drain(0..3);
let mut b_drain = baseline.drain(0..3);
assert_eq!(drain.next(), b_drain.next());
assert_eq!(drain.next(), b_drain.next());
assert_eq!(drain.count(), 1);
}
assert_eq!(vec.len(), 4);
{
let mut drain = vec.drain(2..);
let mut b_drain = baseline.drain(2..);
assert_eq!(drain.next(), b_drain.next());
}
assert!(Iterator::eq(vec.drain(..), baseline.drain(..)));
let mut vec = Vector::new(b"v");
let mut baseline = vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
vec.extend(baseline.clone());
{
let mut drain = vec.drain(1..8);
let mut b_drain = baseline.drain(1..8);
assert_eq!(drain.nth(1), b_drain.nth(1));
assert_eq!(drain.nth_back(2), b_drain.nth_back(2));
assert_eq!(drain.len(), b_drain.len());
}
assert_eq!(vec.len() as usize, baseline.len());
assert!(Iterator::eq(vec.iter(), baseline.iter()));
assert!(Iterator::eq(vec.drain(..), baseline.drain(..)));
crate::mock::with_mocked_blockchain(|m| assert!(m.take_storage().is_empty()));
}
#[test]
fn test_indexing() {
let mut v: Vector<i32> = Vector::new(b"b");
v.push(10);
v.push(20);
assert_eq!(v[0], 10);
assert_eq!(v[1], 20);
let mut x: u32 = 0;
assert_eq!(v[x], 10);
assert_eq!(v[x + 1], 20);
x += 1;
assert_eq!(v[x], 20);
assert_eq!(v[x - 1], 10);
}
#[test]
#[should_panic]
fn test_index_panic() {
let v: Vector<bool> = Vector::new(b"b");
let _ = v[1];
}
#[test]
fn test_index_mut() {
let mut v: Vector<i32> = Vector::new(b"b");
v.push(10);
v.push(20);
*v.index_mut(0) += 1;
assert_eq!(v[0], 11);
assert_eq!(v[1], 20);
let mut x: u32 = 0;
assert_eq!(v[x], 11);
assert_eq!(v[x + 1], 20);
x += 1;
assert_eq!(v[x], 20);
assert_eq!(v[x - 1], 11);
}
#[derive(Arbitrary, Debug)]
enum Op {
Push(u8),
Pop,
Set(u32, u8),
Remove(u32),
Flush,
Reset,
Get(u32),
Swap(u32, u32),
}
#[test]
#[should_panic]
fn test_index_mut_panic() {
let mut v: Vector<bool> = Vector::new(b"b");
v.index_mut(1);
}
#[test]
fn arbitrary() {
setup_free();
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
let mut buf = vec![0; 4096];
for _ in 0..1024 {
crate::mock::with_mocked_blockchain(|b| b.take_storage());
rng.fill_bytes(&mut buf);
let mut sv = Vector::new(b"v");
let mut mv = Vec::new();
let u = Unstructured::new(&buf);
if let Ok(ops) = Vec::<Op>::arbitrary_take_rest(u) {
for op in ops {
match op {
Op::Push(v) => {
sv.push(v);
mv.push(v);
assert_eq!(sv.len() as usize, mv.len());
}
Op::Pop => {
assert_eq!(sv.pop(), mv.pop());
assert_eq!(sv.len() as usize, mv.len());
}
Op::Set(k, v) => {
if sv.is_empty() {
continue;
}
let k = k % sv.len();
sv.set(k, v);
mv[k as usize] = v;
assert_eq!(sv[k], mv[k as usize]);
}
Op::Remove(i) => {
if sv.is_empty() {
continue;
}
let i = i % sv.len();
let r1 = sv.swap_remove(i);
let r2 = mv.swap_remove(i as usize);
assert_eq!(r1, r2);
assert_eq!(sv.len() as usize, mv.len());
}
Op::Flush => {
sv.flush();
}
Op::Reset => {
let serialized = to_vec(&sv).unwrap();
sv = Vector::deserialize(&mut serialized.as_slice()).unwrap();
}
Op::Get(k) => {
let r1 = sv.get(k);
let r2 = mv.get(k as usize);
assert_eq!(r1, r2)
}
Op::Swap(i1, i2) => {
if sv.is_empty() {
continue;
}
let i1 = i1 % sv.len();
let i2 = i2 % sv.len();
sv.swap(i1, i2);
mv.swap(i1 as usize, i2 as usize)
}
}
}
}
assert!(Iterator::eq(sv.iter(), mv.iter()));
}
}
#[test]
fn serialized_bytes() {
use borsh::{BorshDeserialize, BorshSerialize};
let mut vec = Vector::new(b"v".to_vec());
vec.push("Some data");
let serialized = to_vec(&vec).unwrap();
let mut expected_buf = Vec::new();
1u32.serialize(&mut expected_buf).unwrap();
(b"v".to_vec()).serialize(&mut expected_buf).unwrap();
assert_eq!(serialized, expected_buf);
drop(vec);
let vec = Vector::<String>::deserialize(&mut serialized.as_slice()).unwrap();
assert_eq!(vec[0], "Some data");
}
}