From 8d024cc99ca5d32567061e1d8cb43438bde2512f Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 12 Jun 2022 12:42:58 +0200 Subject: [PATCH 1/3] Strict encoding MEdiumVec type supporting up to u24 elements --- Cargo.lock | 2 +- strict_encoding/Cargo.toml | 2 +- strict_encoding/src/collections.rs | 48 ++++++++++++++++++++++++++++++ strict_encoding/src/lib.rs | 2 +- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26766e8f..7d59ebb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -896,7 +896,7 @@ dependencies = [ [[package]] name = "strict_encoding" -version = "1.8.5" +version = "1.8.6" dependencies = [ "amplify", "bitcoin", diff --git a/strict_encoding/Cargo.toml b/strict_encoding/Cargo.toml index 64053b48..acf85092 100644 --- a/strict_encoding/Cargo.toml +++ b/strict_encoding/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "strict_encoding" -version = "1.8.5" +version = "1.8.6" license = "Apache-2.0" authors = ["Dr. Maxim Orlovsky "] description = "Strict encoding: deterministic binary serialization for networking & client-side validation" diff --git a/strict_encoding/src/collections.rs b/strict_encoding/src/collections.rs index 1f7becae..66e9f2e5 100644 --- a/strict_encoding/src/collections.rs +++ b/strict_encoding/src/collections.rs @@ -13,12 +13,15 @@ // software. If not, see . use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::convert::TryFrom; use std::fmt::Debug; use std::hash::Hash; use std::io; use std::io::{Read, Write}; use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; +use amplify::num::u24; + use crate::{Error, StrictDecode, StrictEncode}; /// In terms of strict encoding, ranges are encoded as a tuples of two values: @@ -246,6 +249,51 @@ where } } +/// Wrapper for vectors which may have up to `u32::MAX` elements in strict +/// encoding representation. +#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(transparent) +)] +pub struct MediumVec(Vec) +where + T: Clone + StrictEncode + StrictDecode; + +impl StrictEncode for MediumVec +where + T: Clone + StrictEncode + StrictDecode, +{ + fn strict_encode(&self, mut e: E) -> Result { + let mut len = self.0.len(); + if len > u24::MAX.as_u32() as usize { + return Err(Error::ExceedMaxItems(len)); + } + u24::try_from(len as u32) + .expect("u32 Cmp is broken") + .strict_encode(&mut e)?; + for el in &self.0 { + len += el.strict_encode(&mut e)?; + } + Ok(len) + } +} + +impl StrictDecode for MediumVec +where + T: Clone + StrictDecode + StrictEncode, +{ + fn strict_decode(mut d: D) -> Result { + let len = u24::strict_decode(&mut d)?.as_u32() as usize; + let mut data = Vec::::with_capacity(len); + for _ in 0..len { + data.push(T::strict_decode(&mut d)?); + } + Ok(data.into()) + } +} + /// In terms of strict encoding, `Vec` is stored in form of /// usize-encoded length (see `StrictEncode` implementation for `usize` /// type for encoding platform-independent constant-length diff --git a/strict_encoding/src/lib.rs b/strict_encoding/src/lib.rs index 5e87fbf4..a2dd7372 100644 --- a/strict_encoding/src/lib.rs +++ b/strict_encoding/src/lib.rs @@ -98,7 +98,7 @@ use std::{fmt, io}; #[cfg(feature = "bitcoin")] pub use ::bitcoin::consensus::encode::{ReadExt, WriteExt}; use amplify::IoError; -pub use collections::LargeVec; +pub use collections::{LargeVec, MediumVec}; pub use strategies::Strategy; /// Binary encoding according to the strict rules that usually apply to From 3aa4106b6ca5426086e718d411ab0e7e5c1cfacd Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 12 Jun 2022 13:00:15 +0200 Subject: [PATCH 2/3] Strict encoding: LargeVec guarantees inner vec size --- strict_encoding/src/collections.rs | 95 ++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 12 deletions(-) diff --git a/strict_encoding/src/collections.rs b/strict_encoding/src/collections.rs index 66e9f2e5..315dff4b 100644 --- a/strict_encoding/src/collections.rs +++ b/strict_encoding/src/collections.rs @@ -18,7 +18,9 @@ use std::fmt::Debug; use std::hash::Hash; use std::io; use std::io::{Read, Write}; -use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; +use std::ops::{ + Deref, Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive, +}; use amplify::num::u24; @@ -208,7 +210,7 @@ where /// Wrapper for vectors which may have up to `u32::MAX` elements in strict /// encoding representation. -#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), @@ -216,28 +218,62 @@ where )] pub struct LargeVec(Vec) where - T: Clone + StrictEncode + StrictDecode; + T: StrictEncode + StrictDecode; + +impl Deref for LargeVec +where + T: StrictEncode + StrictDecode, +{ + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl TryFrom> for LargeVec +where + T: StrictEncode + StrictDecode, +{ + type Error = Error; + + fn try_from(value: Vec) -> Result { + let len = value.len(); + if len > u32::MAX as usize { + return Err(Error::ExceedMaxItems(len)); + } + Ok(Self(value)) + } +} + +impl<'me, T> IntoIterator for &'me LargeVec +where + T: StrictEncode + StrictDecode, +{ + type Item = &'me T; + type IntoIter = std::slice::Iter<'me, T>; + + fn into_iter(self) -> Self::IntoIter { self.0.iter() } +} impl StrictEncode for LargeVec where - T: Clone + StrictEncode + StrictDecode, + T: StrictEncode + StrictDecode, { fn strict_encode(&self, mut e: E) -> Result { - let mut len = self.0.len(); + let len = self.0.len(); if len > u32::MAX as usize { return Err(Error::ExceedMaxItems(len)); } - (len as u32).strict_encode(&mut e)?; + let mut count = (len as u32).strict_encode(&mut e)?; for el in &self.0 { - len += el.strict_encode(&mut e)?; + count += el.strict_encode(&mut e)?; } - Ok(len) + Ok(count) } } impl StrictDecode for LargeVec where - T: Clone + StrictDecode + StrictEncode, + T: StrictDecode + StrictEncode, { fn strict_decode(mut d: D) -> Result { let len = u32::strict_decode(&mut d)?; @@ -245,13 +281,48 @@ where for _ in 0..len { data.push(T::strict_decode(&mut d)?); } - Ok(data.into()) + Ok(Self(data)) + } +} + +impl LargeVec +where + T: StrictEncode + StrictDecode, +{ + /// Constructs empty [`LargeVec`]. + pub fn new() -> Self { Self(vec![]) } + + /// Returns the number of elements in the vector, also referred to as its + /// 'length'. + pub fn len_u32(&self) -> u32 { self.0.len() as u32 } + + /// Appends an element to the back of a collection. + /// + /// # Errors + /// + /// Errors with [`Error::ExceedMaxItems`] if the new capacity exceeds + /// `u32::MAX` bytes. + pub fn push(&mut self, item: T) -> Result { + let len = self.0.len(); + if len > u32::MAX as usize { + return Err(Error::ExceedMaxItems(len)); + } + self.0.push(item); + Ok(len) } + + /// Removes and returns the element at position `index` within the vector, + /// shifting all elements after it to the left. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + pub fn remove(&mut self, index: usize) -> T { self.0.remove(index) } } /// Wrapper for vectors which may have up to `u32::MAX` elements in strict /// encoding representation. -#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), @@ -290,7 +361,7 @@ where for _ in 0..len { data.push(T::strict_decode(&mut d)?); } - Ok(data.into()) + Ok(Self(data)) } } From a8711f3ed29e8cdd6e0643379d73e8e2f062384a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 12 Jun 2022 13:07:24 +0200 Subject: [PATCH 3/3] Strict encoding: MediumVec guarantees inner vec size --- Cargo.lock | 2 +- strict_encoding/Cargo.toml | 2 +- strict_encoding/src/collections.rs | 88 +++++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d59ebb0..b950b255 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -896,7 +896,7 @@ dependencies = [ [[package]] name = "strict_encoding" -version = "1.8.6" +version = "1.8.7" dependencies = [ "amplify", "bitcoin", diff --git a/strict_encoding/Cargo.toml b/strict_encoding/Cargo.toml index acf85092..77920131 100644 --- a/strict_encoding/Cargo.toml +++ b/strict_encoding/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "strict_encoding" -version = "1.8.6" +version = "1.8.7" license = "Apache-2.0" authors = ["Dr. Maxim Orlovsky "] description = "Strict encoding: deterministic binary serialization for networking & client-side validation" diff --git a/strict_encoding/src/collections.rs b/strict_encoding/src/collections.rs index 315dff4b..f86ee4c0 100644 --- a/strict_encoding/src/collections.rs +++ b/strict_encoding/src/collections.rs @@ -320,7 +320,7 @@ where pub fn remove(&mut self, index: usize) -> T { self.0.remove(index) } } -/// Wrapper for vectors which may have up to `u32::MAX` elements in strict +/// Wrapper for vectors which may have up to `u24::MAX` elements in strict /// encoding representation. #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] #[cfg_attr( @@ -330,30 +330,64 @@ where )] pub struct MediumVec(Vec) where - T: Clone + StrictEncode + StrictDecode; + T: StrictEncode + StrictDecode; + +impl Deref for MediumVec +where + T: StrictEncode + StrictDecode, +{ + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl TryFrom> for MediumVec +where + T: StrictEncode + StrictDecode, +{ + type Error = Error; + + fn try_from(value: Vec) -> Result { + let len = value.len(); + if len > u32::MAX as usize { + return Err(Error::ExceedMaxItems(len)); + } + Ok(Self(value)) + } +} + +impl<'me, T> IntoIterator for &'me MediumVec +where + T: StrictEncode + StrictDecode, +{ + type Item = &'me T; + type IntoIter = std::slice::Iter<'me, T>; + + fn into_iter(self) -> Self::IntoIter { self.0.iter() } +} impl StrictEncode for MediumVec where - T: Clone + StrictEncode + StrictDecode, + T: StrictEncode + StrictDecode, { fn strict_encode(&self, mut e: E) -> Result { - let mut len = self.0.len(); + let len = self.0.len(); if len > u24::MAX.as_u32() as usize { return Err(Error::ExceedMaxItems(len)); } - u24::try_from(len as u32) + let mut count = u24::try_from(len as u32) .expect("u32 Cmp is broken") .strict_encode(&mut e)?; for el in &self.0 { - len += el.strict_encode(&mut e)?; + count += el.strict_encode(&mut e)?; } - Ok(len) + Ok(count) } } impl StrictDecode for MediumVec where - T: Clone + StrictDecode + StrictEncode, + T: StrictDecode + StrictEncode, { fn strict_decode(mut d: D) -> Result { let len = u24::strict_decode(&mut d)?.as_u32() as usize; @@ -365,6 +399,44 @@ where } } +impl MediumVec +where + T: StrictEncode + StrictDecode, +{ + /// Constructs empty [`LargeVec`]. + pub fn new() -> Self { Self(vec![]) } + + /// Returns the number of elements in the vector, also referred to as its + /// 'length'. + pub fn len_u24(&self) -> u24 { + u24::try_from(self.0.len() as u32) + .expect("MediumVec inner size guarantees are broken") + } + + /// Appends an element to the back of a collection. + /// + /// # Errors + /// + /// Errors with [`Error::ExceedMaxItems`] if the new capacity exceeds + /// `u32::MAX` bytes. + pub fn push(&mut self, item: T) -> Result { + let len = self.0.len(); + if len > u24::MAX.as_u32() as usize { + return Err(Error::ExceedMaxItems(len)); + } + self.0.push(item); + Ok(len) + } + + /// Removes and returns the element at position `index` within the vector, + /// shifting all elements after it to the left. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + pub fn remove(&mut self, index: usize) -> T { self.0.remove(index) } +} + /// In terms of strict encoding, `Vec` is stored in form of /// usize-encoded length (see `StrictEncode` implementation for `usize` /// type for encoding platform-independent constant-length