Skip to content

Commit

Permalink
Merge pull request #82 from LNP-BP/develop
Browse files Browse the repository at this point in the history
Strict encoding MediumVec type supporting up to u24 elements
  • Loading branch information
dr-orlovsky authored Jun 12, 2022
2 parents d7212ad + a8711f3 commit 5d89ff4
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion strict_encoding/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "strict_encoding"
version = "1.8.5"
version = "1.8.7"
license = "Apache-2.0"
authors = ["Dr. Maxim Orlovsky <[email protected]>"]
description = "Strict encoding: deterministic binary serialization for networking & client-side validation"
Expand Down
211 changes: 201 additions & 10 deletions strict_encoding/src/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@
// software. If not, see <https://opensource.org/licenses/Apache-2.0>.

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 std::ops::{
Deref, Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive,
};

use amplify::num::u24;

use crate::{Error, StrictDecode, StrictEncode};

Expand Down Expand Up @@ -205,45 +210,231 @@ 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),
serde(transparent)
)]
pub struct LargeVec<T>(Vec<T>)
where
T: Clone + StrictEncode + StrictDecode;
T: StrictEncode + StrictDecode;

impl<T> Deref for LargeVec<T>
where
T: StrictEncode + StrictDecode,
{
type Target = Vec<T>;

fn deref(&self) -> &Self::Target { &self.0 }
}

impl<T> TryFrom<Vec<T>> for LargeVec<T>
where
T: StrictEncode + StrictDecode,
{
type Error = Error;

fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
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<T>
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<T> StrictEncode for LargeVec<T>
where
T: Clone + StrictEncode + StrictDecode,
T: StrictEncode + StrictDecode,
{
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
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<T> StrictDecode for LargeVec<T>
where
T: Clone + StrictDecode + StrictEncode,
T: StrictDecode + StrictEncode,
{
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let len = u32::strict_decode(&mut d)?;
let mut data = Vec::<T>::with_capacity(len as usize);
for _ in 0..len {
data.push(T::strict_decode(&mut d)?);
}
Ok(data.into())
Ok(Self(data))
}
}

impl<T> LargeVec<T>
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<usize, Error> {
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 `u24::MAX` elements in strict
/// encoding representation.
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(transparent)
)]
pub struct MediumVec<T>(Vec<T>)
where
T: StrictEncode + StrictDecode;

impl<T> Deref for MediumVec<T>
where
T: StrictEncode + StrictDecode,
{
type Target = Vec<T>;

fn deref(&self) -> &Self::Target { &self.0 }
}

impl<T> TryFrom<Vec<T>> for MediumVec<T>
where
T: StrictEncode + StrictDecode,
{
type Error = Error;

fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
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<T>
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<T> StrictEncode for MediumVec<T>
where
T: StrictEncode + StrictDecode,
{
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
let len = self.0.len();
if len > u24::MAX.as_u32() as usize {
return Err(Error::ExceedMaxItems(len));
}
let mut count = u24::try_from(len as u32)
.expect("u32 Cmp is broken")
.strict_encode(&mut e)?;
for el in &self.0 {
count += el.strict_encode(&mut e)?;
}
Ok(count)
}
}

impl<T> StrictDecode for MediumVec<T>
where
T: StrictDecode + StrictEncode,
{
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let len = u24::strict_decode(&mut d)?.as_u32() as usize;
let mut data = Vec::<T>::with_capacity(len);
for _ in 0..len {
data.push(T::strict_decode(&mut d)?);
}
Ok(Self(data))
}
}

impl<T> MediumVec<T>
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<usize, Error> {
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
Expand Down
2 changes: 1 addition & 1 deletion strict_encoding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5d89ff4

Please sign in to comment.