Minor improvements to bounded_vec and defensive. (#10873)

* Fix a few things in bounded_vec

* add test for try_extend

* Update frame/support/src/storage/bounded_vec.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* some review comments

* use swap

* remove clone

* use pop instead of truncate

* remove warn

* review comments

* Update frame/support/src/storage/bounded_vec.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* fix rustdoc

* fix links

* undo link

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Kian Paimani
2022-02-26 08:39:56 +00:00
committed by GitHub
parent 4c984500a7
commit b77d3f917d
5 changed files with 189 additions and 171 deletions
+1 -1
View File
@@ -866,7 +866,7 @@ impl<T: Config> Pallet<T> {
let new_pos = queue.binary_search_by_key(&ayes, |x| x.1).unwrap_or_else(|x| x); let new_pos = queue.binary_search_by_key(&ayes, |x| x.1).unwrap_or_else(|x| x);
branch = if maybe_old_pos.is_none() && new_pos > 0 { branch = if maybe_old_pos.is_none() && new_pos > 0 {
// Just insert. // Just insert.
queue.force_insert_keep_right(new_pos, (index, ayes)); let _ = queue.force_insert_keep_right(new_pos, (index, ayes));
ServiceBranch::RequeuedInsertion ServiceBranch::RequeuedInsertion
} else if let Some(old_pos) = maybe_old_pos { } else if let Some(old_pos) = maybe_old_pos {
// We were in the queue - slide into the correct position. // We were in the queue - slide into the correct position.
+1 -1
View File
@@ -86,7 +86,7 @@ impl<T: Ord, S: Get<u32>> InsertSorted<T> for BoundedVec<T, S> {
mut f: F, mut f: F,
) -> bool { ) -> bool {
let index = self.binary_search_by_key::<K, F>(&f(&t), f).unwrap_or_else(|x| x); let index = self.binary_search_by_key::<K, F>(&f(&t), f).unwrap_or_else(|x| x);
self.force_insert_keep_right(index, t) self.force_insert_keep_right(index, t).is_ok()
} }
} }
@@ -119,7 +119,17 @@ impl<T, S> BoundedVec<T, S> {
self.0 self.0
} }
/// Exactly the same semantics as [`Vec::remove`]. /// Exactly the same semantics as [`slice::sort_by`].
///
/// This is safe since sorting cannot change the number of elements in the vector.
pub fn sort_by<F>(&mut self, compare: F)
where
F: FnMut(&T, &T) -> sp_std::cmp::Ordering,
{
self.0.sort_by(compare)
}
/// Exactly the same semantics as `Vec::remove`.
/// ///
/// # Panics /// # Panics
/// ///
@@ -128,7 +138,7 @@ impl<T, S> BoundedVec<T, S> {
self.0.remove(index) self.0.remove(index)
} }
/// Exactly the same semantics as [`Vec::swap_remove`]. /// Exactly the same semantics as `slice::swap_remove`.
/// ///
/// # Panics /// # Panics
/// ///
@@ -137,12 +147,12 @@ impl<T, S> BoundedVec<T, S> {
self.0.swap_remove(index) self.0.swap_remove(index)
} }
/// Exactly the same semantics as [`Vec::retain`]. /// Exactly the same semantics as `Vec::retain`.
pub fn retain<F: FnMut(&T) -> bool>(&mut self, f: F) { pub fn retain<F: FnMut(&T) -> bool>(&mut self, f: F) {
self.0.retain(f) self.0.retain(f)
} }
/// Exactly the same semantics as [`slice::get_mut`]. /// Exactly the same semantics as `slice::get_mut`.
pub fn get_mut<I: SliceIndex<[T]>>( pub fn get_mut<I: SliceIndex<[T]>>(
&mut self, &mut self,
index: I, index: I,
@@ -150,12 +160,16 @@ impl<T, S> BoundedVec<T, S> {
self.0.get_mut(index) self.0.get_mut(index)
} }
/// Exactly the same semantics as [`Vec::truncate`]. /// Exactly the same semantics as `Vec::truncate`.
///
/// This is safe because `truncate` can never increase the length of the internal vector.
pub fn truncate(&mut self, s: usize) { pub fn truncate(&mut self, s: usize) {
self.0.truncate(s); self.0.truncate(s);
} }
/// Exactly the same semantics as [`Vec::pop`]. /// Exactly the same semantics as `Vec::pop`.
///
/// This is safe since popping can only shrink the inner vector.
pub fn pop(&mut self) -> Option<T> { pub fn pop(&mut self) -> Option<T> {
self.0.pop() self.0.pop()
} }
@@ -191,54 +205,76 @@ impl<T, S: Get<u32>> BoundedVec<T, S> {
S::get() as usize S::get() as usize
} }
/// Forces the insertion of `s` into `self` retaining all items with index at least `index`. /// Returns true of this collection is full.
pub fn is_full(&self) -> bool {
self.len() >= Self::bound()
}
/// Forces the insertion of `element` into `self` retaining all items with index at least
/// `index`.
/// ///
/// If `index == 0` and `self.len() == Self::bound()`, then this is a no-op. /// If `index == 0` and `self.len() == Self::bound()`, then this is a no-op.
/// ///
/// If `Self::bound() < index` or `self.len() < index`, then this is also a no-op. /// If `Self::bound() < index` or `self.len() < index`, then this is also a no-op.
/// ///
/// Returns `true` if the item was inserted. /// Returns `Ok(maybe_removed)` if the item was inserted, where `maybe_removed` is
pub fn force_insert_keep_right(&mut self, index: usize, element: T) -> bool { /// `Some(removed)` if an item was removed to make room for the new one. Returns `Err(())` if
/// `element` cannot be inserted.
pub fn force_insert_keep_right(
&mut self,
index: usize,
mut element: T,
) -> Result<Option<T>, ()> {
// Check against panics. // Check against panics.
if Self::bound() < index || self.len() < index { if Self::bound() < index || self.len() < index {
return false Err(())
} } else if self.len() < Self::bound() {
if self.len() < Self::bound() {
// Cannot panic since self.len() >= index; // Cannot panic since self.len() >= index;
self.0.insert(index, element); self.0.insert(index, element);
Ok(None)
} else { } else {
if index == 0 { if index == 0 {
return false return Err(())
} }
self[0] = element; sp_std::mem::swap(&mut self[0], &mut element);
// `[0..index] cannot panic since self.len() >= index. // `[0..index] cannot panic since self.len() >= index.
// `rotate_left(1)` cannot panic because there is at least 1 element. // `rotate_left(1)` cannot panic because there is at least 1 element.
self[0..index].rotate_left(1); self[0..index].rotate_left(1);
Ok(Some(element))
} }
true
} }
/// Forces the insertion of `s` into `self` retaining all items with index at most `index`. /// Forces the insertion of `element` into `self` retaining all items with index at most
/// `index`.
/// ///
/// If `index == Self::bound()` and `self.len() == Self::bound()`, then this is a no-op. /// If `index == Self::bound()` and `self.len() == Self::bound()`, then this is a no-op.
/// ///
/// If `Self::bound() < index` or `self.len() < index`, then this is also a no-op. /// If `Self::bound() < index` or `self.len() < index`, then this is also a no-op.
/// ///
/// Returns `true` if the item was inserted. /// Returns `Ok(maybe_removed)` if the item was inserted, where `maybe_removed` is
pub fn force_insert_keep_left(&mut self, index: usize, element: T) -> bool { /// `Some(removed)` if an item was removed to make room for the new one. Returns `Err(())` if
/// `element` cannot be inserted.
pub fn force_insert_keep_left(&mut self, index: usize, element: T) -> Result<Option<T>, ()> {
// Check against panics. // Check against panics.
if Self::bound() < index || self.len() < index || Self::bound() == 0 { if Self::bound() < index || self.len() < index || Self::bound() == 0 {
return false return Err(())
} }
// Noop condition. // Noop condition.
if Self::bound() == index && self.len() <= Self::bound() { if Self::bound() == index && self.len() <= Self::bound() {
return false return Err(())
} }
// Cannot panic since `Self.bound() > 0` let maybe_removed = if self.is_full() {
self.0.truncate(Self::bound() - 1); // defensive-only: since we are at capacity, this is a noop.
self.0.truncate(Self::bound());
// if we truncate anything, it will be the last one.
self.0.pop()
} else {
None
};
// Cannot panic since `self.len() >= index`; // Cannot panic since `self.len() >= index`;
self.0.insert(index, element); self.0.insert(index, element);
true Ok(maybe_removed)
} }
/// Move the position of an item from one location to another in the slice. /// Move the position of an item from one location to another in the slice.
@@ -311,6 +347,20 @@ impl<T, S: Get<u32>> BoundedVec<T, S> {
self.0.resize(size, value); self.0.resize(size, value);
} }
/// Exactly the same semantics as [`Vec::extend`], but returns an error and does nothing if the
/// length of the outcome is larger than the bound.
pub fn try_extend(
&mut self,
with: impl IntoIterator<Item = T> + ExactSizeIterator,
) -> Result<(), ()> {
if with.len().saturating_add(self.len()) <= Self::bound() {
self.0.extend(with);
Ok(())
} else {
Err(())
}
}
/// Consumes self and mutates self via the given `mutate` function. /// Consumes self and mutates self via the given `mutate` function.
/// ///
/// If the outcome of mutation is within bounds, `Some(Self)` is returned. Else, `None` is /// If the outcome of mutation is within bounds, `Some(Self)` is returned. Else, `None` is
@@ -522,7 +572,7 @@ where
#[cfg(test)] #[cfg(test)]
pub mod test { pub mod test {
use super::*; use super::*;
use crate::{traits::ConstU32, Twox128}; use crate::{bounded_vec, traits::ConstU32, Twox128};
use sp_io::TestExternalities; use sp_io::TestExternalities;
crate::generate_storage_alias! { Prefix, Foo => Value<BoundedVec<u32, ConstU32<7>>> } crate::generate_storage_alias! { Prefix, Foo => Value<BoundedVec<u32, ConstU32<7>>> }
@@ -534,7 +584,7 @@ pub mod test {
#[test] #[test]
fn slide_works() { fn slide_works() {
let mut b: BoundedVec<u32, ConstU32<6>> = vec![0, 1, 2, 3, 4, 5].try_into().unwrap(); let mut b: BoundedVec<u32, ConstU32<6>> = bounded_vec![0, 1, 2, 3, 4, 5];
assert!(b.slide(1, 5)); assert!(b.slide(1, 5));
assert_eq!(*b, vec![0, 2, 3, 4, 1, 5]); assert_eq!(*b, vec![0, 2, 3, 4, 1, 5]);
assert!(b.slide(4, 0)); assert!(b.slide(4, 0));
@@ -551,7 +601,7 @@ pub mod test {
assert!(!b.slide(7, 0)); assert!(!b.slide(7, 0));
assert_eq!(*b, vec![0, 2, 3, 4, 5, 1]); assert_eq!(*b, vec![0, 2, 3, 4, 5, 1]);
let mut c: BoundedVec<u32, ConstU32<6>> = vec![0, 1, 2].try_into().unwrap(); let mut c: BoundedVec<u32, ConstU32<6>> = bounded_vec![0, 1, 2];
assert!(!c.slide(1, 5)); assert!(!c.slide(1, 5));
assert_eq!(*c, vec![0, 1, 2]); assert_eq!(*c, vec![0, 1, 2]);
assert!(!c.slide(4, 0)); assert!(!c.slide(4, 0));
@@ -564,7 +614,7 @@ pub mod test {
#[test] #[test]
fn slide_noops_work() { fn slide_noops_work() {
let mut b: BoundedVec<u32, ConstU32<6>> = vec![0, 1, 2, 3, 4, 5].try_into().unwrap(); let mut b: BoundedVec<u32, ConstU32<6>> = bounded_vec![0, 1, 2, 3, 4, 5];
assert!(!b.slide(3, 3)); assert!(!b.slide(3, 3));
assert_eq!(*b, vec![0, 1, 2, 3, 4, 5]); assert_eq!(*b, vec![0, 1, 2, 3, 4, 5]);
assert!(!b.slide(3, 4)); assert!(!b.slide(3, 4));
@@ -573,58 +623,59 @@ pub mod test {
#[test] #[test]
fn force_insert_keep_left_works() { fn force_insert_keep_left_works() {
let mut b: BoundedVec<u32, ConstU32<4>> = vec![].try_into().unwrap(); let mut b: BoundedVec<u32, ConstU32<4>> = bounded_vec![];
assert!(!b.force_insert_keep_left(1, 10)); assert_eq!(b.force_insert_keep_left(1, 10), Err(()));
assert!(b.is_empty()); assert!(b.is_empty());
assert!(b.force_insert_keep_left(0, 30)); assert_eq!(b.force_insert_keep_left(0, 30), Ok(None));
assert!(b.force_insert_keep_left(0, 10)); assert_eq!(b.force_insert_keep_left(0, 10), Ok(None));
assert!(b.force_insert_keep_left(1, 20)); assert_eq!(b.force_insert_keep_left(1, 20), Ok(None));
assert!(b.force_insert_keep_left(3, 40)); assert_eq!(b.force_insert_keep_left(3, 40), Ok(None));
assert_eq!(*b, vec![10, 20, 30, 40]); assert_eq!(*b, vec![10, 20, 30, 40]);
// at capacity. // at capacity.
assert!(!b.force_insert_keep_left(4, 41)); assert_eq!(b.force_insert_keep_left(4, 41), Err(()));
assert_eq!(*b, vec![10, 20, 30, 40]); assert_eq!(*b, vec![10, 20, 30, 40]);
assert!(b.force_insert_keep_left(3, 31)); assert_eq!(b.force_insert_keep_left(3, 31), Ok(Some(40)));
assert_eq!(*b, vec![10, 20, 30, 31]); assert_eq!(*b, vec![10, 20, 30, 31]);
assert!(b.force_insert_keep_left(1, 11)); assert_eq!(b.force_insert_keep_left(1, 11), Ok(Some(31)));
assert_eq!(*b, vec![10, 11, 20, 30]); assert_eq!(*b, vec![10, 11, 20, 30]);
assert!(b.force_insert_keep_left(0, 1)); assert_eq!(b.force_insert_keep_left(0, 1), Ok(Some(30)));
assert_eq!(*b, vec![1, 10, 11, 20]); assert_eq!(*b, vec![1, 10, 11, 20]);
let mut z: BoundedVec<u32, ConstU32<0>> = vec![].try_into().unwrap(); let mut z: BoundedVec<u32, ConstU32<0>> = bounded_vec![];
assert!(z.is_empty()); assert!(z.is_empty());
assert!(!z.force_insert_keep_left(0, 10)); assert_eq!(z.force_insert_keep_left(0, 10), Err(()));
assert!(z.is_empty()); assert!(z.is_empty());
} }
#[test] #[test]
fn force_insert_keep_right_works() { fn force_insert_keep_right_works() {
let mut b: BoundedVec<u32, ConstU32<4>> = vec![].try_into().unwrap(); let mut b: BoundedVec<u32, ConstU32<4>> = bounded_vec![];
assert!(!b.force_insert_keep_right(1, 10)); assert_eq!(b.force_insert_keep_right(1, 10), Err(()));
assert!(b.is_empty()); assert!(b.is_empty());
assert!(b.force_insert_keep_right(0, 30)); assert_eq!(b.force_insert_keep_right(0, 30), Ok(None));
assert!(b.force_insert_keep_right(0, 10)); assert_eq!(b.force_insert_keep_right(0, 10), Ok(None));
assert!(b.force_insert_keep_right(1, 20)); assert_eq!(b.force_insert_keep_right(1, 20), Ok(None));
assert!(b.force_insert_keep_right(3, 40)); assert_eq!(b.force_insert_keep_right(3, 40), Ok(None));
assert_eq!(*b, vec![10, 20, 30, 40]); assert_eq!(*b, vec![10, 20, 30, 40]);
// at capacity. // at capacity.
assert!(!b.force_insert_keep_right(0, 0)); assert_eq!(b.force_insert_keep_right(0, 0), Err(()));
assert_eq!(*b, vec![10, 20, 30, 40]); assert_eq!(*b, vec![10, 20, 30, 40]);
assert!(b.force_insert_keep_right(1, 11)); assert_eq!(b.force_insert_keep_right(1, 11), Ok(Some(10)));
assert_eq!(*b, vec![11, 20, 30, 40]); assert_eq!(*b, vec![11, 20, 30, 40]);
assert!(b.force_insert_keep_right(3, 31)); assert_eq!(b.force_insert_keep_right(3, 31), Ok(Some(11)));
assert_eq!(*b, vec![20, 30, 31, 40]); assert_eq!(*b, vec![20, 30, 31, 40]);
assert!(b.force_insert_keep_right(4, 41)); assert_eq!(b.force_insert_keep_right(4, 41), Ok(Some(20)));
assert_eq!(*b, vec![30, 31, 40, 41]); assert_eq!(*b, vec![30, 31, 40, 41]);
assert!(!b.force_insert_keep_right(5, 69)); assert_eq!(b.force_insert_keep_right(5, 69), Err(()));
assert_eq!(*b, vec![30, 31, 40, 41]); assert_eq!(*b, vec![30, 31, 40, 41]);
let mut z: BoundedVec<u32, ConstU32<0>> = vec![].try_into().unwrap(); let mut z: BoundedVec<u32, ConstU32<0>> = bounded_vec![];
assert!(z.is_empty()); assert!(z.is_empty());
assert!(!z.force_insert_keep_right(0, 10)); assert_eq!(z.force_insert_keep_right(0, 10), Err(()));
assert!(z.is_empty()); assert!(z.is_empty());
} }
@@ -636,13 +687,13 @@ pub mod test {
#[test] #[test]
fn decode_len_works() { fn decode_len_works() {
TestExternalities::default().execute_with(|| { TestExternalities::default().execute_with(|| {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap(); let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
Foo::put(bounded); Foo::put(bounded);
assert_eq!(Foo::decode_len().unwrap(), 3); assert_eq!(Foo::decode_len().unwrap(), 3);
}); });
TestExternalities::default().execute_with(|| { TestExternalities::default().execute_with(|| {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap(); let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
FooMap::insert(1, bounded); FooMap::insert(1, bounded);
assert_eq!(FooMap::decode_len(1).unwrap(), 3); assert_eq!(FooMap::decode_len(1).unwrap(), 3);
assert!(FooMap::decode_len(0).is_none()); assert!(FooMap::decode_len(0).is_none());
@@ -650,7 +701,7 @@ pub mod test {
}); });
TestExternalities::default().execute_with(|| { TestExternalities::default().execute_with(|| {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap(); let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
FooDoubleMap::insert(1, 1, bounded); FooDoubleMap::insert(1, 1, bounded);
assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3); assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3);
assert!(FooDoubleMap::decode_len(2, 1).is_none()); assert!(FooDoubleMap::decode_len(2, 1).is_none());
@@ -661,7 +712,7 @@ pub mod test {
#[test] #[test]
fn try_insert_works() { fn try_insert_works() {
let mut bounded: BoundedVec<u32, ConstU32<4>> = vec![1, 2, 3].try_into().unwrap(); let mut bounded: BoundedVec<u32, ConstU32<4>> = bounded_vec![1, 2, 3];
bounded.try_insert(1, 0).unwrap(); bounded.try_insert(1, 0).unwrap();
assert_eq!(*bounded, vec![1, 0, 2, 3]); assert_eq!(*bounded, vec![1, 0, 2, 3]);
@@ -685,13 +736,13 @@ pub mod test {
#[test] #[test]
#[should_panic(expected = "insertion index (is 9) should be <= len (is 3)")] #[should_panic(expected = "insertion index (is 9) should be <= len (is 3)")]
fn try_inert_panics_if_oob() { fn try_inert_panics_if_oob() {
let mut bounded: BoundedVec<u32, ConstU32<4>> = vec![1, 2, 3].try_into().unwrap(); let mut bounded: BoundedVec<u32, ConstU32<4>> = bounded_vec![1, 2, 3];
bounded.try_insert(9, 0).unwrap(); bounded.try_insert(9, 0).unwrap();
} }
#[test] #[test]
fn try_push_works() { fn try_push_works() {
let mut bounded: BoundedVec<u32, ConstU32<4>> = vec![1, 2, 3].try_into().unwrap(); let mut bounded: BoundedVec<u32, ConstU32<4>> = bounded_vec![1, 2, 3];
bounded.try_push(0).unwrap(); bounded.try_push(0).unwrap();
assert_eq!(*bounded, vec![1, 2, 3, 0]); assert_eq!(*bounded, vec![1, 2, 3, 0]);
@@ -700,7 +751,7 @@ pub mod test {
#[test] #[test]
fn deref_coercion_works() { fn deref_coercion_works() {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap(); let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
// these methods come from deref-ed vec. // these methods come from deref-ed vec.
assert_eq!(bounded.len(), 3); assert_eq!(bounded.len(), 3);
assert!(bounded.iter().next().is_some()); assert!(bounded.iter().next().is_some());
@@ -709,7 +760,7 @@ pub mod test {
#[test] #[test]
fn try_mutate_works() { fn try_mutate_works() {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3, 4, 5, 6].try_into().unwrap(); let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3, 4, 5, 6];
let bounded = bounded.try_mutate(|v| v.push(7)).unwrap(); let bounded = bounded.try_mutate(|v| v.push(7)).unwrap();
assert_eq!(bounded.len(), 7); assert_eq!(bounded.len(), 7);
assert!(bounded.try_mutate(|v| v.push(8)).is_none()); assert!(bounded.try_mutate(|v| v.push(8)).is_none());
@@ -717,13 +768,13 @@ pub mod test {
#[test] #[test]
fn slice_indexing_works() { fn slice_indexing_works() {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3, 4, 5, 6].try_into().unwrap(); let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3, 4, 5, 6];
assert_eq!(&bounded[0..=2], &[1, 2, 3]); assert_eq!(&bounded[0..=2], &[1, 2, 3]);
} }
#[test] #[test]
fn vec_eq_works() { fn vec_eq_works() {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3, 4, 5, 6].try_into().unwrap(); let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3, 4, 5, 6];
assert_eq!(bounded, vec![1, 2, 3, 4, 5, 6]); assert_eq!(bounded, vec![1, 2, 3, 4, 5, 6]);
} }
@@ -738,7 +789,7 @@ pub mod test {
#[test] #[test]
fn can_be_collected() { fn can_be_collected() {
let b1: BoundedVec<u32, ConstU32<5>> = vec![1, 2, 3, 4].try_into().unwrap(); let b1: BoundedVec<u32, ConstU32<5>> = bounded_vec![1, 2, 3, 4];
let b2: BoundedVec<u32, ConstU32<5>> = b1.iter().map(|x| x + 1).try_collect().unwrap(); let b2: BoundedVec<u32, ConstU32<5>> = b1.iter().map(|x| x + 1).try_collect().unwrap();
assert_eq!(b2, vec![2, 3, 4, 5]); assert_eq!(b2, vec![2, 3, 4, 5]);
@@ -777,8 +828,8 @@ pub mod test {
#[test] #[test]
fn eq_works() { fn eq_works() {
// of same type // of same type
let b1: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap(); let b1: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
let b2: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap(); let b2: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
assert_eq!(b1, b2); assert_eq!(b1, b2);
// of different type, but same value and bound. // of different type, but same value and bound.
@@ -786,19 +837,41 @@ pub mod test {
B1: u32 = 7; B1: u32 = 7;
B2: u32 = 7; B2: u32 = 7;
} }
let b1: BoundedVec<u32, B1> = vec![1, 2, 3].try_into().unwrap(); let b1: BoundedVec<u32, B1> = bounded_vec![1, 2, 3];
let b2: BoundedVec<u32, B2> = vec![1, 2, 3].try_into().unwrap(); let b2: BoundedVec<u32, B2> = bounded_vec![1, 2, 3];
assert_eq!(b1, b2); assert_eq!(b1, b2);
} }
#[test] #[test]
fn ord_works() { fn ord_works() {
use std::cmp::Ordering; use std::cmp::Ordering;
let b1: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap(); let b1: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
let b2: BoundedVec<u32, ConstU32<7>> = vec![1, 3, 2].try_into().unwrap(); let b2: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 3, 2];
// ordering for vec is lexicographic. // ordering for vec is lexicographic.
assert_eq!(b1.cmp(&b2), Ordering::Less); assert_eq!(b1.cmp(&b2), Ordering::Less);
assert_eq!(b1.cmp(&b2), b1.into_inner().cmp(&b2.into_inner())); assert_eq!(b1.cmp(&b2), b1.into_inner().cmp(&b2.into_inner()));
} }
#[test]
fn try_extend_works() {
let mut b: BoundedVec<u32, ConstU32<5>> = bounded_vec![1, 2, 3];
assert!(b.try_extend(vec![4].into_iter()).is_ok());
assert_eq!(*b, vec![1, 2, 3, 4]);
assert!(b.try_extend(vec![5].into_iter()).is_ok());
assert_eq!(*b, vec![1, 2, 3, 4, 5]);
assert!(b.try_extend(vec![6].into_iter()).is_err());
assert_eq!(*b, vec![1, 2, 3, 4, 5]);
let mut b: BoundedVec<u32, ConstU32<5>> = bounded_vec![1, 2, 3];
assert!(b.try_extend(vec![4, 5].into_iter()).is_ok());
assert_eq!(*b, vec![1, 2, 3, 4, 5]);
let mut b: BoundedVec<u32, ConstU32<5>> = bounded_vec![1, 2, 3];
assert!(b.try_extend(vec![4, 5, 6].into_iter()).is_err());
assert_eq!(*b, vec![1, 2, 3]);
}
} }
+2
View File
@@ -58,6 +58,8 @@ pub use misc::{
PreimageRecipient, PrivilegeCmp, SameOrOther, Time, TryCollect, TryDrop, UnixTime, PreimageRecipient, PrivilegeCmp, SameOrOther, Time, TryCollect, TryDrop, UnixTime,
WrapperKeepOpaque, WrapperOpaque, WrapperKeepOpaque, WrapperOpaque,
}; };
#[doc(hidden)]
pub use misc::{DEFENSIVE_OP_INTERNAL_ERROR, DEFENSIVE_OP_PUBLIC_ERROR};
mod stored_map; mod stored_map;
pub use stored_map::{StorageMapShim, StoredMap}; pub use stored_map::{StorageMapShim, StoredMap};
+43 -100
View File
@@ -24,8 +24,34 @@ use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, Saturating};
use sp_runtime::{traits::Block as BlockT, DispatchError}; use sp_runtime::{traits::Block as BlockT, DispatchError};
use sp_std::{cmp::Ordering, prelude::*}; use sp_std::{cmp::Ordering, prelude::*};
const DEFENSIVE_OP_PUBLIC_ERROR: &'static str = "a defensive failure has been triggered; please report the block number at https://github.com/paritytech/substrate/issues"; #[doc(hidden)]
const DEFENSIVE_OP_INTERNAL_ERROR: &'static str = "Defensive failure has been triggered!"; pub const DEFENSIVE_OP_PUBLIC_ERROR: &'static str = "a defensive failure has been triggered; please report the block number at https://github.com/paritytech/substrate/issues";
#[doc(hidden)]
pub const DEFENSIVE_OP_INTERNAL_ERROR: &'static str = "Defensive failure has been triggered!";
/// Generic function to mark an execution path as ONLY defensive.
///
/// Similar to mark a match arm or `if/else` branch as `unreachable!`.
#[macro_export]
macro_rules! defensive {
() => {
frame_support::log::error!(
target: "runtime",
"{}",
$crate::traits::misc::DEFENSIVE_OP_PUBLIC_ERROR
);
debug_assert!(false, "{}", $crate::traits::misc::DEFENSIVE_OP_INTERNAL_ERROR);
};
($error:tt) => {
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
$crate::traits::misc::DEFENSIVE_OP_PUBLIC_ERROR,
$error
);
debug_assert!(false, "{}: {:?}", $crate::traits::misc::DEFENSIVE_OP_INTERNAL_ERROR, $error);
}
}
/// Prelude module for all defensive traits to be imported at once. /// Prelude module for all defensive traits to be imported at once.
pub mod defensive_prelude { pub mod defensive_prelude {
@@ -116,12 +142,7 @@ impl<T> Defensive<T> for Option<T> {
match self { match self {
Some(inner) => inner, Some(inner) => inner,
None => { None => {
debug_assert!(false, "{}", DEFENSIVE_OP_INTERNAL_ERROR); defensive!();
frame_support::log::error!(
target: "runtime",
"{}",
DEFENSIVE_OP_PUBLIC_ERROR
);
or or
}, },
} }
@@ -131,12 +152,7 @@ impl<T> Defensive<T> for Option<T> {
match self { match self {
Some(inner) => inner, Some(inner) => inner,
None => { None => {
debug_assert!(false, "{}", DEFENSIVE_OP_INTERNAL_ERROR); defensive!();
frame_support::log::error!(
target: "runtime",
"{}",
DEFENSIVE_OP_PUBLIC_ERROR
);
f() f()
}, },
} }
@@ -149,12 +165,7 @@ impl<T> Defensive<T> for Option<T> {
match self { match self {
Some(inner) => inner, Some(inner) => inner,
None => { None => {
debug_assert!(false, "{}", DEFENSIVE_OP_INTERNAL_ERROR); defensive!();
frame_support::log::error!(
target: "runtime",
"{}",
DEFENSIVE_OP_PUBLIC_ERROR
);
Default::default() Default::default()
}, },
} }
@@ -164,12 +175,7 @@ impl<T> Defensive<T> for Option<T> {
match self { match self {
Some(inner) => Some(inner), Some(inner) => Some(inner),
None => { None => {
debug_assert!(false, "{}", DEFENSIVE_OP_INTERNAL_ERROR); defensive!();
frame_support::log::error!(
target: "runtime",
"{}",
DEFENSIVE_OP_PUBLIC_ERROR
);
None None
}, },
} }
@@ -181,13 +187,7 @@ impl<T, E: sp_std::fmt::Debug> Defensive<T> for Result<T, E> {
match self { match self {
Ok(inner) => inner, Ok(inner) => inner,
Err(e) => { Err(e) => {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
or or
}, },
} }
@@ -197,13 +197,7 @@ impl<T, E: sp_std::fmt::Debug> Defensive<T> for Result<T, E> {
match self { match self {
Ok(inner) => inner, Ok(inner) => inner,
Err(e) => { Err(e) => {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
f() f()
}, },
} }
@@ -216,13 +210,7 @@ impl<T, E: sp_std::fmt::Debug> Defensive<T> for Result<T, E> {
match self { match self {
Ok(inner) => inner, Ok(inner) => inner,
Err(e) => { Err(e) => {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
Default::default() Default::default()
}, },
} }
@@ -232,13 +220,7 @@ impl<T, E: sp_std::fmt::Debug> Defensive<T> for Result<T, E> {
match self { match self {
Ok(inner) => Ok(inner), Ok(inner) => Ok(inner),
Err(e) => { Err(e) => {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
Err(e) Err(e)
}, },
} }
@@ -248,13 +230,7 @@ impl<T, E: sp_std::fmt::Debug> Defensive<T> for Result<T, E> {
impl<T, E: sp_std::fmt::Debug> DefensiveResult<T, E> for Result<T, E> { impl<T, E: sp_std::fmt::Debug> DefensiveResult<T, E> for Result<T, E> {
fn defensive_map_err<F, O: FnOnce(E) -> F>(self, o: O) -> Result<T, F> { fn defensive_map_err<F, O: FnOnce(E) -> F>(self, o: O) -> Result<T, F> {
self.map_err(|e| { self.map_err(|e| {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
o(e) o(e)
}) })
} }
@@ -262,13 +238,7 @@ impl<T, E: sp_std::fmt::Debug> DefensiveResult<T, E> for Result<T, E> {
fn defensive_map_or_else<U, D: FnOnce(E) -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { fn defensive_map_or_else<U, D: FnOnce(E) -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
self.map_or_else( self.map_or_else(
|e| { |e| {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
default(e) default(e)
}, },
f, f,
@@ -279,13 +249,7 @@ impl<T, E: sp_std::fmt::Debug> DefensiveResult<T, E> for Result<T, E> {
match self { match self {
Ok(inner) => Some(inner), Ok(inner) => Some(inner),
Err(e) => { Err(e) => {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
None None
}, },
} }
@@ -295,13 +259,7 @@ impl<T, E: sp_std::fmt::Debug> DefensiveResult<T, E> for Result<T, E> {
match self { match self {
Ok(inner) => Ok(f(inner)), Ok(inner) => Ok(f(inner)),
Err(e) => { Err(e) => {
debug_assert!(false, "{}: {:?}", DEFENSIVE_OP_INTERNAL_ERROR, e); defensive!(e);
frame_support::log::error!(
target: "runtime",
"{}: {:?}",
DEFENSIVE_OP_PUBLIC_ERROR,
e
);
Err(e) Err(e)
}, },
} }
@@ -312,12 +270,7 @@ impl<T> DefensiveOption<T> for Option<T> {
fn defensive_map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { fn defensive_map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
self.map_or_else( self.map_or_else(
|| { || {
debug_assert!(false, "{}", DEFENSIVE_OP_INTERNAL_ERROR); defensive!();
frame_support::log::error!(
target: "runtime",
"{}",
DEFENSIVE_OP_PUBLIC_ERROR,
);
default() default()
}, },
f, f,
@@ -326,12 +279,7 @@ impl<T> DefensiveOption<T> for Option<T> {
fn defensive_ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> { fn defensive_ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
self.ok_or_else(|| { self.ok_or_else(|| {
debug_assert!(false, "{}", DEFENSIVE_OP_INTERNAL_ERROR); defensive!();
frame_support::log::error!(
target: "runtime",
"{}",
DEFENSIVE_OP_PUBLIC_ERROR,
);
err() err()
}) })
} }
@@ -340,12 +288,7 @@ impl<T> DefensiveOption<T> for Option<T> {
match self { match self {
Some(inner) => Some(f(inner)), Some(inner) => Some(f(inner)),
None => { None => {
debug_assert!(false, "{}", DEFENSIVE_OP_INTERNAL_ERROR); defensive!();
frame_support::log::error!(
target: "runtime",
"{}",
DEFENSIVE_OP_PUBLIC_ERROR,
);
None None
}, },
} }