use core::ops::Range;
use std::fmt::{self, Display, Formatter};
use std::io;
pub use bitcoin::consensus::encode::{ReadExt, WriteExt};
pub trait StrictEncode {
type Error: std::error::Error + From<Error>;
fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Self::Error>;
}
pub trait StrictDecode: Sized {
type Error: std::error::Error + From<Error>;
fn strict_decode<D: io::Read>(d: D) -> Result<Self, Self::Error>;
}
pub fn strict_encode<T>(data: &T) -> Result<Vec<u8>, T::Error>
where
T: StrictEncode,
T::Error: std::error::Error + From<Error>,
{
let mut encoder = io::Cursor::new(vec![]);
data.strict_encode(&mut encoder)?;
Ok(encoder.into_inner())
}
pub fn strict_decode<T>(data: &impl AsRef<[u8]>) -> Result<T, T::Error>
where
T: StrictDecode,
T::Error: std::error::Error + From<Error>,
{
let mut decoder = io::Cursor::new(data);
let rv = T::strict_decode(&mut decoder)?;
let consumed = decoder.position() as usize;
if consumed == data.as_ref().len() {
Ok(rv)
} else {
Err(Error::DataNotEntirelyConsumed)?
}
}
#[derive(Clone, PartialEq, Eq, Debug, From, Error)]
pub enum Error {
Io(io::ErrorKind),
#[from(std::str::Utf8Error)]
#[from(std::string::FromUtf8Error)]
Utf8Conversion,
ExceedMaxItems(usize),
WrongOptionalEncoding(u8),
EnumValueOverflow(String),
EnumValueNotKnown(String, u8),
UnsupportedDataStructure(&'static str),
ValueOutOfRange(&'static str, Range<u128>, u128),
RepeatedValue(String),
DataNotEntirelyConsumed,
DataIntegrityError(String),
}
impl From<Error> for fmt::Error {
fn from(_: Error) -> Self {
fmt::Error
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::Io(err.kind())
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
use Error::*;
match self {
Io(kind) => write!(f, "I/O error: {:?}", kind),
Utf8Conversion => write!(f, "String data are not in valid UTF-8 encoding"),
ExceedMaxItems(size) => write!(
f,
"A collection (slice, vector or other type) has {} items, which \
exceeds maximum allowed value for `u16` type representing \
collection size according to LNPBP-6 spec)",
size
),
WrongOptionalEncoding(significator) => write!(
f,
"Invalid value {} met as a significator byte, which must be \
equal to either 0 (no value) or 1",
significator
),
EnumValueOverflow(enum_name) => write!(
f,
"Enums are encoded as a `u8`-based values; the provided enum {} \
has underlying primitive type that does not fit into `u8` value",
enum_name
),
EnumValueNotKnown(enum_name, value) => write!(
f,
"An unsupported value {} for enum {} encountered during decode \
operation",
value, enum_name
),
UnsupportedDataStructure(details) => write!(
f,
"The data are correct, however their structure indicate that \
they were created with the future software version which has \
functional absent in the current implementation. Here is more \
details: {}",
details
),
ValueOutOfRange(data_type, range, value) => write!(
f,
"Decoding resulted in value {} for type {} that exceeds the \
supported range {:#?}",
value, data_type, range
),
RepeatedValue(value) => write!(
f,
"A repeated value {} found during set collection deserialization",
value
),
DataNotEntirelyConsumed => write!(
f,
"Data were not consumed entirely during strict decoding procedure"
),
DataIntegrityError(str) => write!(f, "Data integrity error: {}", str),
}
}
}
#[macro_export]
macro_rules! strict_encode_list {
( $encoder:ident; $($item:expr),+ ) => {
{
let mut len = 0usize;
$(
len += $item.strict_encode(&mut $encoder)?;
)+
len
}
};
( $encoder:ident; $len:ident; $($item:expr),+ ) => {
{
$(
$len += $item.strict_encode(&mut $encoder)?;
)+
$len
}
}
}
#[macro_export]
macro_rules! strict_decode_self {
( $decoder:ident; $($item:ident),+ ) => {
{
Self {
$(
$item: StrictDecode::strict_decode(&mut $decoder)?,
)+
}
}
};
}
#[macro_export]
macro_rules! impl_enum_strict_encoding {
($type:ty) => {
impl $crate::strict_encoding::StrictEncode for $type {
type Error = $crate::strict_encoding::Error;
#[inline]
fn strict_encode<E: ::std::io::Write>(
&self,
e: E,
) -> Result<usize, Self::Error> {
use ::num_traits::ToPrimitive;
match self.to_u8() {
Some(result) => result.strict_encode(e),
None => {
Err($crate::strict_encoding::Error::EnumValueOverflow(
stringify!($type).to_string(),
))
}
}
}
}
impl $crate::strict_encoding::StrictDecode for $type {
type Error = $crate::strict_encoding::Error;
#[inline]
fn strict_decode<D: ::std::io::Read>(
d: D,
) -> Result<Self, Self::Error> {
use ::num_traits::FromPrimitive;
let value = u8::strict_decode(d)?;
match Self::from_u8(value) {
Some(result) => Ok(result),
None => {
Err($crate::strict_encoding::Error::EnumValueNotKnown(
stringify!($type).to_string(),
value,
))
}
}
}
}
};
}
pub mod strategies {
use super::{Error, StrictDecode, StrictEncode};
use amplify::Wrapper;
use std::io;
pub struct HashFixedBytes;
pub struct BitcoinConsensus;
pub struct Wrapped;
pub trait Strategy {
type Strategy;
}
impl<T> StrictEncode for T
where
T: Strategy + Clone,
amplify::Holder<T, <T as Strategy>::Strategy>: StrictEncode,
{
type Error = <amplify::Holder<T, T::Strategy> as StrictEncode>::Error;
#[inline]
fn strict_encode<E: io::Write>(
&self,
e: E,
) -> Result<usize, Self::Error> {
amplify::Holder::new(self.clone()).strict_encode(e)
}
}
impl<T> StrictDecode for T
where
T: Strategy,
amplify::Holder<T, <T as Strategy>::Strategy>: StrictDecode,
{
type Error = <amplify::Holder<T, T::Strategy> as StrictDecode>::Error;
#[inline]
fn strict_decode<D: io::Read>(d: D) -> Result<Self, Self::Error> {
Ok(amplify::Holder::strict_decode(d)?.into_inner())
}
}
impl<T> StrictEncode for amplify::Holder<T, Wrapped>
where
T: Wrapper,
T::Inner: StrictEncode,
{
type Error = <T::Inner as StrictEncode>::Error;
#[inline]
fn strict_encode<E: io::Write>(
&self,
e: E,
) -> Result<usize, Self::Error> {
Ok(self.as_inner().to_inner().strict_encode(e)?)
}
}
impl<T> StrictDecode for amplify::Holder<T, Wrapped>
where
T: Wrapper,
T::Inner: StrictDecode,
{
type Error = <T::Inner as StrictDecode>::Error;
#[inline]
fn strict_decode<D: io::Read>(d: D) -> Result<Self, Self::Error> {
Ok(Self::new(T::from_inner(T::Inner::strict_decode(d)?)))
}
}
impl<T> StrictEncode for amplify::Holder<T, HashFixedBytes>
where
T: bitcoin::hashes::Hash,
{
type Error = Error;
#[inline]
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
e.write_all(&self.as_inner()[..])?;
Ok(T::LEN)
}
}
impl<T> StrictDecode for amplify::Holder<T, HashFixedBytes>
where
T: bitcoin::hashes::Hash,
{
type Error = Error;
#[inline]
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let mut buf = vec![0u8; T::LEN];
d.read_exact(&mut buf)?;
Ok(Self::new(T::from_slice(&buf)?))
}
}
impl<T> StrictEncode for amplify::Holder<T, BitcoinConsensus>
where
T: bitcoin::consensus::Encodable,
{
type Error = Error;
#[inline]
fn strict_encode<E: io::Write>(
&self,
e: E,
) -> Result<usize, Self::Error> {
self.as_inner().consensus_encode(e).map_err(Error::from)
}
}
impl<T> StrictDecode for amplify::Holder<T, BitcoinConsensus>
where
T: bitcoin::consensus::Decodable,
{
type Error = Error;
#[inline]
fn strict_decode<D: io::Read>(d: D) -> Result<Self, Self::Error> {
Ok(Self::new(T::consensus_decode(d).map_err(Error::from)?))
}
}
impl From<bitcoin::hashes::Error> for Error {
#[inline]
fn from(_: bitcoin::hashes::Error) -> Self {
Error::DataIntegrityError("Incorrect hash length".to_string())
}
}
impl From<bitcoin::consensus::encode::Error> for Error {
#[inline]
fn from(e: bitcoin::consensus::encode::Error) -> Self {
if let bitcoin::consensus::encode::Error::Io(err) = e {
Error::Io(err.kind())
} else {
Error::DataIntegrityError(e.to_string())
}
}
}
}
pub use strategies::Strategy;
mod number_little_endian {
use super::{strategies, Error, Strategy, StrictDecode, StrictEncode};
use bitcoin::util::uint::{Uint128, Uint256};
use std::io;
impl Strategy for u8 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for u16 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for u32 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for u64 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for Uint128 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for Uint256 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for i8 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for i16 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for i32 {
type Strategy = strategies::BitcoinConsensus;
}
impl Strategy for i64 {
type Strategy = strategies::BitcoinConsensus;
}
impl StrictEncode for bool {
type Error = Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Error> {
(*self as u8).strict_encode(&mut e)
}
}
impl StrictDecode for bool {
type Error = Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
match u8::strict_decode(&mut d)? {
0 => Ok(false),
1 => Ok(true),
v => Err(Error::ValueOutOfRange("boolean", 0..1, v as u128)),
}
}
}
impl StrictEncode for usize {
type Error = Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Error> {
if *self > core::u16::MAX as usize {
Err(Error::ExceedMaxItems(*self))?;
}
let size = *self as u16;
size.strict_encode(&mut e)
}
}
impl StrictDecode for usize {
type Error = Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
u16::strict_decode(&mut d).map(|val| val as usize)
}
}
impl StrictEncode for f32 {
type Error = Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Error> {
e.write_all(&self.to_le_bytes())?;
Ok(4)
}
}
impl StrictDecode for f32 {
type Error = Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let mut buf: [u8; 4] = [0; 4];
d.read_exact(&mut buf)?;
Ok(Self::from_le_bytes(buf))
}
}
impl StrictEncode for f64 {
type Error = Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Error> {
e.write_all(&self.to_le_bytes())?;
Ok(8)
}
}
impl StrictDecode for f64 {
type Error = Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let mut buf: [u8; 8] = [0; 8];
d.read_exact(&mut buf)?;
Ok(Self::from_le_bytes(buf))
}
}
}
mod byte_strings {
use super::{Error, StrictDecode, StrictEncode};
use std::io;
use std::ops::Deref;
impl StrictEncode for &[u8] {
type Error = Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Error> {
let mut len = self.len();
len += len.strict_encode(&mut e)?;
e.write_all(self)?;
Ok(len)
}
}
impl StrictEncode for [u8; 32] {
type Error = Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Error> {
e.write_all(self)?;
Ok(self.len())
}
}
impl StrictDecode for [u8; 32] {
type Error = Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let mut ret = [0u8; 32];
d.read_exact(&mut ret)?;
Ok(ret)
}
}
impl StrictEncode for Box<[u8]> {
type Error = Error;
fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
self.deref().strict_encode(e)
}
}
impl StrictDecode for Box<[u8]> {
type Error = Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let len = usize::strict_decode(&mut d)?;
let mut ret = vec![0u8; len];
d.read_exact(&mut ret)?;
Ok(ret.into_boxed_slice())
}
}
impl StrictEncode for &str {
type Error = Error;
fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
self.as_bytes().strict_encode(e)
}
}
impl StrictEncode for String {
type Error = Error;
fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
self.as_bytes().strict_encode(e)
}
}
impl StrictDecode for String {
type Error = Error;
fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
String::from_utf8(Vec::<u8>::strict_decode(d)?).map_err(Error::from)
}
}
}
mod compositional_types {
use super::{Error, StrictDecode, StrictEncode};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::Debug;
use std::hash::Hash;
use std::io;
impl<T> StrictEncode for Option<T>
where
T: StrictEncode,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
Ok(match self {
None => strict_encode_list!(e; 0u8),
Some(val) => strict_encode_list!(e; 1u8, val),
})
}
}
impl<T> StrictDecode for Option<T>
where
T: StrictDecode,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let len = u8::strict_decode(&mut d)?;
match len {
0 => Ok(None),
1 => Ok(Some(T::strict_decode(&mut d)?)),
invalid => Err(Error::WrongOptionalEncoding(invalid))?,
}
}
}
impl<T> StrictEncode for Vec<T>
where
T: StrictEncode,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
let len = self.len() as usize;
let mut encoded = len.strict_encode(&mut e)?;
for item in self {
encoded += item.strict_encode(&mut e)?;
}
Ok(encoded)
}
}
impl<T> StrictDecode for Vec<T>
where
T: StrictDecode,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let len = usize::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)
}
}
impl<T> StrictEncode for HashSet<T>
where
T: StrictEncode + Eq + Ord + Hash + Debug,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
let len = self.len() as usize;
let mut encoded = len.strict_encode(&mut e)?;
let mut vec: Vec<&T> = self.iter().collect();
vec.sort();
for item in vec {
encoded += item.strict_encode(&mut e)?;
}
Ok(encoded)
}
}
impl<T> StrictDecode for HashSet<T>
where
T: StrictDecode + Eq + Ord + Hash + Debug,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let len = usize::strict_decode(&mut d)?;
let mut data = HashSet::<T>::with_capacity(len as usize);
for _ in 0..len {
let val = T::strict_decode(&mut d)?;
if data.contains(&val) {
Err(Error::RepeatedValue(format!("{:?}", val)))?;
} else {
data.insert(val);
}
}
Ok(data)
}
}
impl<T> StrictEncode for BTreeSet<T>
where
T: StrictEncode + Eq + Ord + Debug,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
let len = self.len() as usize;
let mut encoded = len.strict_encode(&mut e)?;
let mut vec: Vec<&T> = self.iter().collect();
vec.sort();
for item in vec {
encoded += item.strict_encode(&mut e)?;
}
Ok(encoded)
}
}
impl<T> StrictDecode for BTreeSet<T>
where
T: StrictDecode + Eq + Ord + Debug,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let len = usize::strict_decode(&mut d)?;
let mut data = BTreeSet::<T>::new();
for _ in 0..len {
let val = T::strict_decode(&mut d)?;
if data.contains(&val) {
Err(Error::RepeatedValue(format!("{:?}", val)))?;
} else {
data.insert(val);
}
}
Ok(data)
}
}
impl<T> StrictEncode for HashMap<usize, T>
where
T: StrictEncode + Clone,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
let ordered: BTreeMap<usize, T> =
self.iter().map(|(key, val)| (*key, val.clone())).collect();
ordered.strict_encode(&mut e)
}
}
impl<T> StrictDecode for HashMap<usize, T>
where
T: StrictDecode + Clone,
T::Error: From<Error>,
{
type Error = T::Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let map: HashMap<usize, T> =
BTreeMap::<usize, T>::strict_decode(&mut d)?
.iter()
.map(|(key, val)| (*key, val.clone()))
.collect();
Ok(map)
}
}
impl<K, V> StrictEncode for BTreeMap<K, V>
where
K: StrictEncode + Ord + Clone,
V: StrictEncode + Clone,
K::Error: From<Error>,
V::Error: From<Error> + From<K::Error>,
{
type Error = V::Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
let len = self.len() as usize;
let encoded = len.strict_encode(&mut e)?;
self.iter().try_fold(encoded, |mut acc, (key, val)| {
acc += key.strict_encode(&mut e)?;
acc += val.strict_encode(&mut e)?;
Ok(acc)
})
}
}
impl<K, V> StrictDecode for BTreeMap<K, V>
where
K: StrictDecode + Ord + Clone,
V: StrictDecode + Clone,
K::Error: From<Error>,
V::Error: From<Error> + From<K::Error>,
{
type Error = V::Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let len = usize::strict_decode(&mut d)?;
let mut map = BTreeMap::<K, V>::new();
for _ in 0..len {
let key = K::strict_decode(&mut d)?;
let val = V::strict_decode(&mut d)?;
map.insert(key, val);
}
Ok(map)
}
}
impl<K, V> StrictEncode for (K, V)
where
K: StrictEncode + Clone,
V: StrictEncode + Clone,
K::Error: From<Error>,
V::Error: From<Error> + From<K::Error>,
{
type Error = V::Error;
fn strict_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, Self::Error> {
Ok(self.0.strict_encode(&mut e)? + self.1.strict_encode(&mut e)?)
}
}
impl<K, V> StrictDecode for (K, V)
where
K: StrictDecode + Clone,
V: StrictDecode + Clone,
K::Error: From<Error>,
V::Error: From<Error> + From<K::Error>,
{
type Error = V::Error;
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Self::Error> {
let a = K::strict_decode(&mut d)?;
let b = V::strict_decode(&mut d)?;
Ok((a, b))
}
}
}
#[cfg(test)]
#[macro_use]
pub mod test {
use std::fmt::Debug;
use std::fs::File;
use std::io::{BufWriter, Write};
use super::*;
macro_rules! test_enum_u8_exhaustive {
($enum:ident; $( $item:path => $val:expr ),+) => { {
use ::num_traits::{FromPrimitive, ToPrimitive};
$( assert_eq!($item.to_u8().unwrap(), $val); )+
$( assert_eq!($enum::from_u8($val).unwrap(), $item); )+
let mut set = ::std::collections::HashSet::new();
$( set.insert($val); )+
for x in 0..=core::u8::MAX {
if !set.contains(&x) {
assert_eq!($enum::from_u8(x), None);
let decoded: Result<$enum, _> = $crate::strict_encoding::strict_decode(&[x]);
assert_eq!(decoded.unwrap_err(), $crate::strict_encoding::Error::EnumValueNotKnown(stringify!($enum).to_string(), x));
}
}
let mut all = ::std::collections::BTreeSet::new();
$( all.insert($item); )+
for (idx, a) in all.iter().enumerate() {
assert_eq!(a, a);
for b in all.iter().skip(idx + 1) {
assert_ne!(a, b);
assert!(a < b);
}
}
$( assert_eq!($crate::strict_encoding::strict_encode(&$item).unwrap(), &[$val]); )+
$( assert_eq!($item, $crate::strict_encoding::strict_decode(&[$val]).unwrap()); )+
} };
}
#[macro_export]
macro_rules! test_encode {
($(($x:ident, $ty:ty)),*) => (
{
$(
let object = <$ty>::strict_decode(&$x[..]).unwrap();
test_suite(&object, &$x[..], $x.to_vec().len());
)*
}
);
}
#[macro_export]
macro_rules! test_garbage_exhaustive {
($range:expr; $( ($x:ident, $ty:ty, $err:ident) ),+ ) => (
{$(
let mut cp = $x.clone();
for byte in $range {
cp[0] = byte as u8;
assert_eq!(
<$ty>::strict_decode(&cp[..]).unwrap_err(),
crate::paradigms::strict_encoding::Error::EnumValueNotKnown($err.to_string(), byte)
);
}
)+}
);
}
pub fn print_bytes<T: StrictEncode + StrictDecode>(object: &T) {
let mut buf = vec![];
object.strict_encode(&mut buf).unwrap();
println!("{:#x?}", buf);
}
pub fn print_to_file<T: StrictEncode + StrictDecode>(
object: &T,
) -> Result<usize, Box<dyn std::error::Error>> {
let write_file = File::create("./enocded.txt").unwrap();
let mut writer = BufWriter::new(&write_file);
let mut buf = vec![];
let written = object.strict_encode(&mut buf).unwrap();
writeln!(&mut writer, "{:#x?}", buf)?;
Ok(written)
}
pub fn encode_decode<T: StrictEncode + StrictDecode>(object: &T) {
let mut encoded_object: Vec<u8> = vec![];
object.strict_encode(&mut encoded_object).unwrap();
T::strict_decode(&encoded_object[..]).unwrap();
}
pub fn test_suite<T: StrictEncode + StrictDecode + PartialEq + Debug>(
object: &T,
test_vec: &[u8],
test_size: usize,
) -> T {
let mut encoded_object: Vec<u8> = vec![];
let write_1 = object.strict_encode(&mut encoded_object).unwrap();
let decoded_object = T::strict_decode(&encoded_object[..]).unwrap();
assert_eq!(write_1, test_size);
assert_eq!(decoded_object, *object);
encoded_object.clear();
let write_2 =
decoded_object.strict_encode(&mut encoded_object).unwrap();
assert_eq!(encoded_object, test_vec);
assert_eq!(write_2, test_size);
decoded_object
}
fn gen_strings() -> Vec<&'static str> {
vec![
"",
"0",
" ",
"A string slice (&str) is made of bytes (u8), and a byte slice \
(&[u8]) is made of bytes, so this function converts between the two.\
Not all byte slices are valid string slices, however: &str requires \
that it is valid UTF-8. from_utf8() checks to ensure that the bytes \
are valid UTF-8, and then does the conversion.",
]
}
#[test]
fn test_encode_decode() {
gen_strings().into_iter().for_each(|s| {
let r = strict_encode(&s).unwrap();
let p: String = strict_decode(&r).unwrap();
assert_eq!(s, p);
})
}
#[test]
#[should_panic(expected = "DataNotEntirelyConsumed")]
fn test_consumation() {
gen_strings().into_iter().for_each(|s| {
let mut r = strict_encode(&s).unwrap();
r.extend_from_slice("data".as_ref());
let _: String = strict_decode(&r).unwrap();
})
}
#[test]
fn test_error_propagation() {
gen_strings().into_iter().for_each(|s| {
let r = strict_encode(&s).unwrap();
let p: Result<String, _> = strict_decode(&r[..1].to_vec());
assert!(p.is_err());
})
}
#[test]
fn test_u8_encode() {
let zero: u8 = 0;
let one: u8 = 1;
let thirteen: u8 = 13;
let confusing: u8 = 0xEF;
let nearly_full: u8 = 0xFE;
let full: u8 = 0xFF;
let byte_0 = &[0u8][..];
let byte_1 = &[1u8][..];
let byte_13 = &[13u8][..];
let byte_ef = &[0xEFu8][..];
let byte_fe = &[0xFEu8][..];
let byte_ff = &[0xFFu8][..];
assert_eq!(strict_encode(&zero).unwrap(), byte_0);
assert_eq!(strict_encode(&one).unwrap(), byte_1);
assert_eq!(strict_encode(&thirteen).unwrap(), byte_13);
assert_eq!(strict_encode(&confusing).unwrap(), byte_ef);
assert_eq!(strict_encode(&nearly_full).unwrap(), byte_fe);
assert_eq!(strict_encode(&full).unwrap(), byte_ff);
assert_eq!(u8::strict_decode(byte_0).unwrap(), zero);
assert_eq!(u8::strict_decode(byte_1).unwrap(), one);
assert_eq!(u8::strict_decode(byte_13).unwrap(), thirteen);
assert_eq!(u8::strict_decode(byte_ef).unwrap(), confusing);
assert_eq!(u8::strict_decode(byte_fe).unwrap(), nearly_full);
assert_eq!(u8::strict_decode(byte_ff).unwrap(), full);
}
#[test]
fn test_option_encode_none() {
let o1: Option<u8> = None;
let o2: Option<u64> = None;
let two_zero_bytes = &vec![0u8][..];
assert_eq!(strict_encode(&o1).unwrap(), two_zero_bytes);
assert_eq!(strict_encode(&o2).unwrap(), two_zero_bytes);
assert_eq!(Option::<u8>::strict_decode(two_zero_bytes).unwrap(), None);
assert_eq!(Option::<u64>::strict_decode(two_zero_bytes).unwrap(), None);
}
#[test]
fn test_option_encode_some() {
let o1: Option<u8> = Some(0);
let o2: Option<u8> = Some(13);
let o3: Option<u8> = Some(0xFF);
let o4: Option<u64> = Some(13);
let o5: Option<u64> = Some(0x1FF);
let o6: Option<u64> = Some(0xFFFFFFFFFFFFFFFF);
let o7: Option<usize> = Some(13);
let o8: Option<usize> = Some(0xFFFFFFFFFFFFFFFF);
let byte_0 = &[1u8, 0u8][..];
let byte_13 = &[1u8, 13u8][..];
let byte_255 = &[1u8, 0xFFu8][..];
let word_13 = &[1u8, 13u8, 0u8][..];
let qword_13 = &[1u8, 13u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..];
let qword_256 =
&[1u8, 0xFFu8, 0x01u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..];
let qword_max = &[
1u8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8, 0xFFu8,
][..];
assert_eq!(strict_encode(&o1).unwrap(), byte_0);
assert_eq!(strict_encode(&o2).unwrap(), byte_13);
assert_eq!(strict_encode(&o3).unwrap(), byte_255);
assert_eq!(strict_encode(&o4).unwrap(), qword_13);
assert_eq!(strict_encode(&o5).unwrap(), qword_256);
assert_eq!(strict_encode(&o6).unwrap(), qword_max);
assert_eq!(strict_encode(&o7).unwrap(), word_13);
assert!(strict_encode(&o8).err().is_some());
assert_eq!(Option::<u8>::strict_decode(byte_0).unwrap(), Some(0));
assert_eq!(Option::<u8>::strict_decode(byte_13).unwrap(), Some(13));
assert_eq!(Option::<u8>::strict_decode(byte_255).unwrap(), Some(0xFF));
assert_eq!(Option::<u64>::strict_decode(qword_13).unwrap(), Some(13));
assert_eq!(
Option::<u64>::strict_decode(qword_256).unwrap(),
Some(0x1FF)
);
assert_eq!(
Option::<u64>::strict_decode(qword_max).unwrap(),
Some(0xFFFFFFFFFFFFFFFF)
);
assert_eq!(Option::<usize>::strict_decode(word_13).unwrap(), Some(13));
assert_eq!(
Option::<usize>::strict_decode(qword_max).unwrap(),
Some(0xFFFF)
);
}
#[test]
fn test_option_decode_vec() {
assert!(Option::<u8>::strict_decode(&[2u8, 0u8, 0u8, 0u8][..])
.err()
.is_some());
assert!(Option::<u8>::strict_decode(&[3u8, 0u8, 0u8, 0u8][..])
.err()
.is_some());
assert!(Option::<u8>::strict_decode(&[0xFFu8, 0u8, 0u8, 0u8][..])
.err()
.is_some());
}
#[test]
fn test_vec_encode() {
let v1: Vec<u8> = vec![0, 13, 0xFF];
let v2: Vec<u8> = vec![13];
let v3: Vec<u64> = vec![0, 13, 13, 0x1FF, 0xFFFFFFFFFFFFFFFF];
let v4: Vec<u8> =
(0..0x1FFFF).map(|item| (item % 0xFF) as u8).collect();
let s1 = &[3u8, 0u8, 0u8, 13u8, 0xFFu8][..];
let s2 = &[1u8, 0u8, 13u8][..];
let s3 = &[
5u8, 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 13, 0,
0, 0, 0, 0, 0, 0, 0xFF, 1, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
][..];
assert_eq!(strict_encode(&v1).unwrap(), s1);
assert_eq!(strict_encode(&v2).unwrap(), s2);
assert_eq!(strict_encode(&v3).unwrap(), s3);
assert!(strict_encode(&v4).err().is_some());
assert_eq!(Vec::<u8>::strict_decode(s1).unwrap(), v1);
assert_eq!(Vec::<u8>::strict_decode(s2).unwrap(), v2);
assert_eq!(Vec::<u64>::strict_decode(s3).unwrap(), v3);
}
}