#![allow(non_upper_case_globals)]
use std::fmt::Display;
use std::marker::PhantomData;
use std::mem::size_of;
use std::ptr;
use std::slice;
use num_traits::{FromPrimitive, PrimInt, Unsigned};
use pl_hlist::*;
use crate::byte_vector;
use crate::byte_vector::ByteVector;
use crate::error::Error;
pub trait Codec {
type Value;
fn encode(&self, value: &Self::Value) -> EncodeResult;
fn decode(&self, bv: &ByteVector) -> DecodeResult<Self::Value>;
}
pub type EncodeResult = Result<ByteVector, Error>;
#[derive(Debug)]
pub struct DecoderResult<V> {
pub value: V,
pub remainder: ByteVector,
}
pub type DecodeResult<V> = Result<DecoderResult<V>, Error>;
impl<C: Codec + ?Sized> Codec for Box<C> {
type Value = C::Value;
#[inline(always)]
fn encode(&self, value: &Self::Value) -> EncodeResult {
(**self).encode(value)
}
#[inline(always)]
fn decode(&self, bv: &ByteVector) -> DecodeResult<Self::Value> {
(**self).decode(bv)
}
}
impl<C: Codec + ?Sized> Codec for &'static C {
type Value = C::Value;
#[inline(always)]
fn encode(&self, value: &Self::Value) -> EncodeResult {
(*self).encode(value)
}
#[inline(always)]
fn decode(&self, bv: &ByteVector) -> DecodeResult<Self::Value> {
(*self).decode(bv)
}
}
macro_rules! integral_codec {
{ $structname:ident, $value:ident, $encswap:expr, $decswap:expr } => {
struct $structname<T> {
_marker: PhantomData<T>
}
impl<T> Codec for $structname<T>
where T: PrimInt
{
type Value = T;
fn encode(&self, $value: &T) -> EncodeResult {
let size = size_of::<T>();
let mut v = [0u8; byte_vector::DIRECT_VALUE_SIZE_LIMIT];
unsafe {
let src_ptr: *const u8 = ($encswap as *const T) as *const u8;
let dst_ptr: *mut u8 = v.as_mut_ptr();
ptr::copy(src_ptr, dst_ptr, size);
}
Ok(byte_vector::from_slice(v, size))
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<T> {
let size = size_of::<T>();
let mut $value: T = T::zero();
return unsafe {
let dst_ptr: *mut u8 = (&mut $value as *mut T) as *mut u8;
let mut buf = slice::from_raw_parts_mut(dst_ptr, size);
bv.read(&mut buf, 0, size).and_then(|_size| {
bv.drop(size).map(|remainder| {
DecoderResult { value: $decswap, remainder }
})
})
}
}
}
}
}
integral_codec!(IntegralCodec, value, value, value);
integral_codec!(IntegralBECodec, value, &(*value).to_be(), value.to_be());
integral_codec!(IntegralLECodec, value, &(*value).to_le(), value.to_le());
pub const uint8: &'static dyn Codec<Value = u8> = &IntegralCodec {
_marker: PhantomData::<u8>,
};
pub const int8: &'static dyn Codec<Value = i8> = &IntegralCodec {
_marker: PhantomData::<i8>,
};
pub const uint16: &'static dyn Codec<Value = u16> = &IntegralBECodec {
_marker: PhantomData::<u16>,
};
pub const int16: &'static dyn Codec<Value = i16> = &IntegralBECodec {
_marker: PhantomData::<i16>,
};
pub const uint32: &'static dyn Codec<Value = u32> = &IntegralBECodec {
_marker: PhantomData::<u32>,
};
pub const int32: &'static dyn Codec<Value = i32> = &IntegralBECodec {
_marker: PhantomData::<i32>,
};
pub const uint64: &'static dyn Codec<Value = u64> = &IntegralBECodec {
_marker: PhantomData::<u64>,
};
pub const int64: &'static dyn Codec<Value = i64> = &IntegralBECodec {
_marker: PhantomData::<i64>,
};
pub const uint16_l: &'static dyn Codec<Value = u16> = &IntegralLECodec {
_marker: PhantomData::<u16>,
};
pub const int16_l: &'static dyn Codec<Value = i16> = &IntegralLECodec {
_marker: PhantomData::<i16>,
};
pub const uint32_l: &'static dyn Codec<Value = u32> = &IntegralLECodec {
_marker: PhantomData::<u32>,
};
pub const int32_l: &'static dyn Codec<Value = i32> = &IntegralLECodec {
_marker: PhantomData::<i32>,
};
pub const uint64_l: &'static dyn Codec<Value = u64> = &IntegralLECodec {
_marker: PhantomData::<u64>,
};
pub const int64_l: &'static dyn Codec<Value = i64> = &IntegralLECodec {
_marker: PhantomData::<i64>,
};
#[inline(always)]
pub fn ignore(len: usize) -> impl Codec<Value = ()> {
IgnoreCodec { len }
}
struct IgnoreCodec {
len: usize,
}
impl Codec for IgnoreCodec {
type Value = ();
fn encode(&self, _value: &()) -> EncodeResult {
Ok(byte_vector::fill(0, self.len))
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<()> {
bv.drop(self.len).map(|remainder| DecoderResult {
value: (),
remainder,
})
}
}
#[inline(always)]
pub fn constant(bytes: &ByteVector) -> impl Codec<Value = ()> {
ConstantCodec {
bytes: (*bytes).clone(),
}
}
struct ConstantCodec {
bytes: ByteVector,
}
impl Codec for ConstantCodec {
type Value = ();
fn encode(&self, _value: &()) -> EncodeResult {
Ok(self.bytes.clone())
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<()> {
bv.take(self.bytes.length()).and_then(|taken| {
if taken == self.bytes {
Ok(DecoderResult {
value: (),
remainder: bv.drop(self.bytes.length()).unwrap(),
})
} else {
Err(Error::new(format!(
"Expected constant {:?} but got {:?}",
self.bytes, taken
)))
}
})
}
}
#[inline(always)]
pub fn identity_bytes() -> impl Codec<Value = ByteVector> {
IdentityCodec
}
struct IdentityCodec;
impl Codec for IdentityCodec {
type Value = ByteVector;
fn encode(&self, value: &ByteVector) -> EncodeResult {
Ok((*value).clone())
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<ByteVector> {
Ok(DecoderResult {
value: (*bv).clone(),
remainder: byte_vector::empty(),
})
}
}
#[inline(always)]
pub fn bytes(len: usize) -> impl Codec<Value = ByteVector> {
fixed_size_bytes(len, identity_bytes())
}
#[inline(always)]
pub fn fixed_size_bytes<T, C>(len: usize, codec: C) -> impl Codec<Value = T>
where
C: Codec<Value = T>,
{
FixedSizeCodec { len, codec }
}
struct FixedSizeCodec<C> {
len: usize,
codec: C,
}
impl<T, C> Codec for FixedSizeCodec<C>
where
C: Codec<Value = T>,
{
type Value = T;
fn encode(&self, value: &T) -> EncodeResult {
self.codec.encode(value).and_then(|encoded| {
if encoded.length() > self.len {
Err(Error::new(format!(
"Encoding requires {} bytes but codec is limited to fixed length of {}",
encoded.length(),
self.len
)))
} else {
encoded.pad_right(self.len)
}
})
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<T> {
forcomp!({
taken <- bv.take(self.len);
decoded <- self.codec.decode(&taken);
} yield {
DecoderResult { value: decoded.value, remainder: bv.drop(self.len).unwrap() }
})
}
}
#[inline(always)]
pub fn variable_size_bytes<L, V, LC, VC>(len_codec: LC, val_codec: VC) -> impl Codec<Value = V>
where
L: PrimInt + Unsigned + FromPrimitive + Display,
LC: Codec<Value = L>,
VC: Codec<Value = V>,
{
VariableSizeCodec {
len_codec,
val_codec,
}
}
struct VariableSizeCodec<LC, VC> {
len_codec: LC,
val_codec: VC,
}
impl<L, V, LC, VC> Codec for VariableSizeCodec<LC, VC>
where
L: PrimInt + Unsigned + FromPrimitive + Display,
LC: Codec<Value = L>,
VC: Codec<Value = V>,
{
type Value = V;
fn encode(&self, value: &V) -> EncodeResult {
self.val_codec.encode(&value).and_then(|encoded_val| {
match L::from_usize(encoded_val.length()) {
Some(len) => self.len_codec.encode(&len).map(|encoded_len| byte_vector::append(&encoded_len, &encoded_val)),
None => Err(Error::new(format!("Length of encoded value ({} bytes) is greater than maximum value ({}) of length type", encoded_val.length(), L::max_value())))
}
})
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<V> {
forcomp!({
decoded_len <- self.len_codec.decode(&bv);
remainder <- {
let len = decoded_len.value.to_usize().unwrap();
decoded_len.remainder.take(len)
};
decoded_val <- self.val_codec.decode(&remainder);
} yield {
DecoderResult { value: decoded_val.value, remainder: bv.drop(remainder.length()).unwrap() }
})
}
}
#[inline(always)]
pub fn eager<C>(bv_codec: C) -> impl Codec<Value = Vec<u8>>
where
C: Codec<Value = ByteVector>,
{
EagerCodec { bv_codec }
}
struct EagerCodec<C> {
bv_codec: C,
}
impl<C> Codec for EagerCodec<C>
where
C: Codec<Value = ByteVector>,
{
type Value = Vec<u8>;
fn encode(&self, value: &Vec<u8>) -> EncodeResult {
self.bv_codec.encode(&byte_vector::from_slice_copy(value))
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<Vec<u8>> {
forcomp!({
decoded <- self.bv_codec.decode(bv);
vec <- decoded.value.to_vec();
} yield {
DecoderResult { value: vec, remainder: decoded.remainder }
})
}
}
#[inline(always)]
pub fn hnil_codec() -> impl Codec<Value = HNil> {
HNilCodec
}
struct HNilCodec;
impl Codec for HNilCodec {
type Value = HNil;
fn encode(&self, _value: &HNil) -> EncodeResult {
Ok(byte_vector::empty())
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<HNil> {
Ok(DecoderResult {
value: HNil,
remainder: bv.clone(),
})
}
}
#[inline(always)]
pub fn hlist_prepend_codec<H, T, HC, TC>(
head_codec: HC,
tail_codec: TC,
) -> impl Codec<Value = HCons<H, T>>
where
T: HList,
HC: Codec<Value = H>,
TC: Codec<Value = T>,
{
HListPrependCodec {
head_codec,
tail_codec,
}
}
struct HListPrependCodec<HC, TC> {
head_codec: HC,
tail_codec: TC,
}
impl<H, T, HC, TC> Codec for HListPrependCodec<HC, TC>
where
T: HList,
HC: Codec<Value = H>,
TC: Codec<Value = T>,
{
type Value = HCons<H, T>;
fn encode(&self, value: &HCons<H, T>) -> EncodeResult {
forcomp!({
encoded_head <- self.head_codec.encode(&value.head());
encoded_tail <- self.tail_codec.encode(&value.tail());
} yield {
byte_vector::append(&encoded_head, &encoded_tail)
})
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<HCons<H, T>> {
forcomp!({
decoded_head <- self.head_codec.decode(&bv);
decoded_tail <- self.tail_codec.decode(&decoded_head.remainder);
} yield {
DecoderResult { value: HCons(decoded_head.value, decoded_tail.value), remainder: decoded_tail.remainder }
})
}
}
#[inline(always)]
pub fn hlist_flat_prepend_codec<H, T, HC, TC, F>(
head_codec: HC,
tail_codec_fn: F,
) -> impl Codec<Value = HCons<H, T>>
where
T: HList,
HC: Codec<Value = H>,
TC: Codec<Value = T>,
F: Fn(&H) -> TC,
{
HListFlatPrependCodec {
head_codec,
tail_codec_fn,
}
}
struct HListFlatPrependCodec<HC, F> {
head_codec: HC,
tail_codec_fn: F,
}
impl<H, T, HC, TC, F> Codec for HListFlatPrependCodec<HC, F>
where
T: HList,
HC: Codec<Value = H>,
TC: Codec<Value = T>,
F: Fn(&H) -> TC,
{
type Value = HCons<H, T>;
fn encode(&self, value: &HCons<H, T>) -> EncodeResult {
forcomp!({
encoded_head <- self.head_codec.encode(&value.head());
encoded_tail <- (self.tail_codec_fn)(&value.head()).encode(&value.tail());
} yield {
byte_vector::append(&encoded_head, &encoded_tail)
})
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<HCons<H, T>> {
forcomp!({
decoded_head <- self.head_codec.decode(&bv);
decoded_tail <- (self.tail_codec_fn)(&decoded_head.value).decode(&decoded_head.remainder);
} yield {
DecoderResult { value: HCons(decoded_head.value, decoded_tail.value), remainder: decoded_tail.remainder }
})
}
}
#[inline(always)]
pub fn struct_codec<H, S, HC>(hlist_codec: HC) -> impl Codec<Value = S>
where
H: HList,
S: FromHList<H> + ToHList<H>,
HC: Codec<Value = H>,
{
RecordStructCodec {
hlist_codec,
_marker: PhantomData::<S>,
}
}
struct RecordStructCodec<S, HC> {
hlist_codec: HC,
_marker: PhantomData<S>,
}
impl<H, S, HC> Codec for RecordStructCodec<S, HC>
where
H: HList,
S: FromHList<H> + ToHList<H>,
HC: Codec<Value = H>,
{
type Value = S;
fn encode(&self, value: &S) -> EncodeResult {
self.hlist_codec.encode(&value.to_hlist())
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<S> {
self.hlist_codec.decode(bv).map(|decoded| DecoderResult {
value: S::from_hlist(decoded.value),
remainder: decoded.remainder,
})
}
}
#[inline(always)]
pub fn with_context<T, C>(context: &'static str, codec: C) -> impl Codec<Value = T>
where
C: Codec<Value = T>,
{
ContextCodec { codec, context }
}
struct ContextCodec<C> {
codec: C,
context: &'static str,
}
impl<T, C> Codec for ContextCodec<C>
where
C: Codec<Value = T>,
{
type Value = T;
fn encode(&self, value: &T) -> EncodeResult {
self.codec
.encode(value)
.map_err(|e| e.push_context(self.context))
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<T> {
self.codec
.decode(bv)
.map_err(|e| e.push_context(self.context))
}
}
#[inline(always)]
pub fn drop_left<T, LC, RC>(lhs: LC, rhs: RC) -> impl Codec<Value = T>
where
LC: Codec<Value = ()>,
RC: Codec<Value = T>,
{
DropLeftCodec { lhs, rhs }
}
struct DropLeftCodec<LC, RC> {
lhs: LC,
rhs: RC,
}
impl<T, LC, RC> Codec for DropLeftCodec<LC, RC>
where
LC: Codec<Value = ()>,
RC: Codec<Value = T>,
{
type Value = T;
fn encode(&self, value: &T) -> EncodeResult {
forcomp!({
encoded_lhs <- self.lhs.encode(&());
encoded_rhs <- self.rhs.encode(value);
} yield {
byte_vector::append(&encoded_lhs, &encoded_rhs)
})
}
fn decode(&self, bv: &ByteVector) -> DecodeResult<T> {
self.lhs
.decode(bv)
.and_then(|decoded| self.rhs.decode(&decoded.remainder))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Debug;
#[test]
fn forcomp_macro_should_work() {
let v1 = forcomp!({
part1 <- Some(1u8);
} yield { part1 });
assert!(v1.is_some());
let v2 = forcomp!({
part1 <- Some(1u8);
part2 <- None::<u8>;
} yield { part1 + part2 });
assert!(v2.is_none());
let v3 = forcomp!({
part1 <- Some(1u8);
part2 <- Some(2u8);
} yield { part1 + part2 });
assert_eq!(v3.unwrap(), 3u8);
}
fn assert_round_trip<T, C>(codec: C, value: &T, raw_bytes: &Option<ByteVector>)
where
T: 'static + Eq + Debug,
C: Codec<Value = T>,
{
let result = codec.encode(value).and_then(|encoded| {
let compare_result = match *raw_bytes {
Some(ref expected) => {
if encoded != *expected {
Err(Error::new(format!(
"Encoded bytes {:?} do not match expected bytes {:?}",
encoded, *expected
)))
} else {
Ok(())
}
}
None => Ok(()),
};
if let Err(error) = compare_result {
return Err(error);
}
codec.decode(&encoded).map(|decoded| decoded.value)
});
match result {
Ok(decoded) => assert_eq!(decoded, *value),
Err(e) => panic!("Round-trip encoding failed: {}", e.message()),
}
}
#[test]
fn a_u8_value_should_round_trip() {
assert_round_trip(uint8, &7, &Some(byte_vector!(7)));
}
#[test]
fn an_i8_value_should_round_trip() {
assert_round_trip(int8, &7, &Some(byte_vector!(7)));
assert_round_trip(int8, &-2, &Some(byte_vector!(0xfe)));
assert_round_trip(int8, &-16, &Some(byte_vector!(0xf0)));
assert_round_trip(int8, &-128, &Some(byte_vector!(0x80)));
}
#[test]
fn a_u16_value_should_round_trip() {
assert_round_trip(uint16, &0x1234, &Some(byte_vector!(0x12, 0x34)));
assert_round_trip(uint16_l, &0x1234, &Some(byte_vector!(0x34, 0x12)));
}
#[test]
fn an_i16_value_should_round_trip() {
assert_round_trip(int16, &0x1234, &Some(byte_vector!(0x12, 0x34)));
assert_round_trip(int16, &-2, &Some(byte_vector!(0xff, 0xfe)));
assert_round_trip(int16_l, &0x1234, &Some(byte_vector!(0x34, 0x12)));
assert_round_trip(int16_l, &-2, &Some(byte_vector!(0xfe, 0xff)));
}
#[test]
fn a_u32_value_should_round_trip() {
assert_round_trip(
uint32,
&0x1234_5678,
&Some(byte_vector!(0x12, 0x34, 0x56, 0x78)),
);
assert_round_trip(
uint32_l,
&0x1234_5678,
&Some(byte_vector!(0x78, 0x56, 0x34, 0x12)),
);
}
#[test]
fn an_i32_value_should_round_trip() {
assert_round_trip(
int32,
&0x1234_5678,
&Some(byte_vector!(0x12, 0x34, 0x56, 0x78)),
);
assert_round_trip(int32, &-2, &Some(byte_vector!(0xff, 0xff, 0xff, 0xfe)));
assert_round_trip(
int32_l,
&0x1234_5678,
&Some(byte_vector!(0x78, 0x56, 0x34, 0x12)),
);
assert_round_trip(int32_l, &-2, &Some(byte_vector!(0xfe, 0xff, 0xff, 0xff)));
}
#[test]
fn a_u64_value_should_round_trip() {
assert_round_trip(
uint64,
&0x1234_5678_90ab_cdef,
&Some(byte_vector!(0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef)),
);
assert_round_trip(
uint64_l,
&0x1234_5678_90ab_cdef,
&Some(byte_vector!(0xef, 0xcd, 0xab, 0x90, 0x78, 0x56, 0x34, 0x12)),
);
}
#[test]
fn an_i64_value_should_round_trip() {
assert_round_trip(
int64,
&0x1234_5678_90ab_cdef,
&Some(byte_vector!(0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef)),
);
assert_round_trip(
int64,
&-2,
&Some(byte_vector!(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe)),
);
assert_round_trip(
int64_l,
&0x1234_5678_90ab_cdef,
&Some(byte_vector!(0xef, 0xcd, 0xab, 0x90, 0x78, 0x56, 0x34, 0x12)),
);
assert_round_trip(
int64_l,
&-2,
&Some(byte_vector!(0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)),
);
}
#[test]
fn an_ignore_codec_should_round_trip() {
assert_round_trip(ignore(4), &(), &Some(byte_vector!(0, 0, 0, 0)));
}
#[test]
fn decoding_with_ignore_codec_should_succeed_if_the_input_vector_is_long_enough() {
let input = byte_vector!(7, 1, 2, 3, 4);
let codec = ignore(3);
match codec.decode(&input) {
Ok(result) => {
let expected_remainder = byte_vector!(3, 4);
assert_eq!(expected_remainder, result.remainder);
}
Err(e) => panic!("Decoding failed: {}", e.message()),
}
}
#[test]
fn decoding_with_ignore_codec_should_fail_if_the_input_vector_is_smaller_than_the_ignored_length(
) {
let input = byte_vector!(1u8);
let codec = ignore(3);
assert_eq!(
codec.decode(&input).unwrap_err().message(),
"Requested length of 3 bytes exceeds vector length of 1"
);
}
#[test]
fn a_constant_codec_should_round_trip() {
let input = byte_vector!(1, 2, 3, 4);
assert_round_trip(constant(&input), &(), &Some(input));
}
#[test]
fn decoding_with_constant_codec_should_fail_if_the_input_vector_does_not_match_the_constant_vector(
) {
let input = byte_vector!(1, 2, 3, 4);
let codec = constant(&byte_vector!(6, 6, 6));
assert_eq!(
codec.decode(&input).unwrap_err().message(),
"Expected constant 060606 but got 010203"
);
}
#[test]
fn decoding_with_constant_codec_should_fail_if_the_input_vector_is_smaller_than_the_constant_vector(
) {
let input = byte_vector!(1);
let codec = constant(&byte_vector!(6, 6, 6));
assert_eq!(
codec.decode(&input).unwrap_err().message(),
"Requested view offset of 0 and length 3 bytes exceeds vector length of 1"
);
}
#[test]
fn an_identity_codec_should_round_trip() {
let input = byte_vector!(1, 2, 3, 4);
assert_round_trip(identity_bytes(), &input, &Some(input.clone()));
}
#[test]
fn a_byte_vector_codec_should_round_trip() {
let input = byte_vector!(7, 1, 2, 3, 4);
assert_round_trip(bytes(5), &input, &Some(input.clone()));
}
#[test]
fn decoding_with_byte_vector_codec_should_return_remainder_that_had_len_bytes_dropped() {
let input = byte_vector!(7, 1, 2, 3, 4);
let codec = bytes(3);
match codec.decode(&input) {
Ok(result) => {
assert_eq!(result.value, byte_vector!(7, 1, 2));
assert_eq!(result.remainder, byte_vector!(3, 4));
}
Err(e) => panic!("Decoding failed: {}", e.message()),
}
}
#[test]
fn decoding_with_byte_vector_codec_should_fail_when_vector_has_less_space_than_given_length() {
let input = byte_vector!(1, 2);
let codec = bytes(4);
assert_eq!(
codec.decode(&input).unwrap_err().message(),
"Requested view offset of 0 and length 4 bytes exceeds vector length of 2"
);
}
#[test]
fn a_fixed_size_bytes_codec_should_round_trip() {
let codec = fixed_size_bytes(1, uint8);
assert_round_trip(codec, &7u8, &Some(byte_vector!(7)));
}
#[test]
fn encoding_with_fixed_size_codec_should_pad_with_zeros_when_value_is_smaller_than_given_length(
) {
let codec = fixed_size_bytes(3, uint8);
assert_round_trip(codec, &7u8, &Some(byte_vector!(7, 0, 0)));
}
#[test]
fn encoding_with_fixed_size_codec_should_fail_when_value_needs_more_space_than_given_length() {
let codec = fixed_size_bytes(1, constant(&byte_vector!(6, 6, 6)));
assert_eq!(
codec.encode(&()).unwrap_err().message(),
"Encoding requires 3 bytes but codec is limited to fixed length of 1"
);
}
#[test]
fn decoding_with_fixed_size_codec_should_return_remainder_that_had_len_bytes_dropped() {
let input = byte_vector!(7, 1, 2, 3, 4);
let codec = fixed_size_bytes(3, uint8);
match codec.decode(&input) {
Ok(result) => {
assert_eq!(result.value, 7u8);
assert_eq!(result.remainder, byte_vector!(3, 4));
}
Err(e) => panic!("Decoding failed: {}", e.message()),
}
}
#[test]
fn decoding_with_fixed_size_codec_should_fail_when_vector_has_less_space_than_given_length() {
let input = byte_vector!(1, 2);
let codec = fixed_size_bytes(4, bytes(6));
assert_eq!(
codec.decode(&input).unwrap_err().message(),
"Requested view offset of 0 and length 4 bytes exceeds vector length of 2"
);
}
#[test]
fn a_variable_size_bytes_codec_should_round_trip() {
let input = byte_vector!(7, 1, 2, 3, 4);
let codec = variable_size_bytes(uint16, identity_bytes());
assert_round_trip(codec, &input, &Some(byte_vector!(0, 5, 7, 1, 2, 3, 4)));
}
#[test]
fn encoding_with_variable_size_codec_should_fail_when_length_of_encoded_value_is_too_large() {
let input = byte_vector::fill(0x7, 256);
let codec = variable_size_bytes(uint8, identity_bytes());
assert_eq!(codec.encode(&input).unwrap_err().message(), "Length of encoded value (256 bytes) is greater than maximum value (255) of length type");
}
#[test]
fn an_eager_codec_should_round_trip() {
let input = vec![7, 1, 2, 3, 4];
let codec = eager(variable_size_bytes(uint16, identity_bytes()));
assert_round_trip(codec, &input, &Some(byte_vector!(0, 5, 7, 1, 2, 3, 4)));
}
#[allow(unused_parens)]
#[test]
fn context_should_be_pushed_when_using_the_bitor_operator() {
let input = byte_vector::empty();
let codec = with_context(
"section",
with_context("header", with_context("magic", uint8)),
);
assert_eq!(codec.decode(&input).unwrap_err().message(), "section/header/magic: Requested read offset of 0 and length 1 bytes exceeds vector length of 0");
}
#[test]
fn an_hnil_codec_should_round_trip() {
assert_round_trip(hnil_codec(), &HNil, &Some(byte_vector::empty()));
}
#[test]
fn an_hlist_prepend_codec_should_round_trip() {
let codec1 = hlist_prepend_codec(uint8, hnil_codec());
assert_round_trip(codec1, &hlist!(7u8), &Some(byte_vector!(7)));
let codec2 = hlist_prepend_codec(uint8, hlist_prepend_codec(uint8, hnil_codec()));
assert_round_trip(codec2, &hlist!(7u8, 3u8), &Some(byte_vector!(7, 3)));
}
#[test]
fn an_hlist_flat_prepend_codec_should_round_trip() {
let codec = hlist_flat_prepend_codec(
uint8,
|header| hcodec!({bytes((*header) as usize)} :: {uint16}),
);
assert_round_trip(
codec,
&hlist!(0x02u8, byte_vector!(0xAB, 0xCD), 0xCAFEu16),
&Some(byte_vector!(0x02, 0xAB, 0xCD, 0xCA, 0xFE)),
);
}
#[test]
fn an_hlist_codec_should_round_trip() {
let codec = hcodec!({uint8} :: {uint8} :: {uint8});
assert_round_trip(codec, &hlist!(7u8, 3u8, 1u8), &Some(byte_vector!(7, 3, 1)));
}
#[test]
fn the_hcodec_macro_should_support_drop_left() {
let c = byte_vector!(0xCA, 0xFE);
let codec = hcodec!(
{ "magic" => constant(&c) } >>
{ "field1" => uint8 } ::
{ "field2" => uint8 }
);
let bytes = byte_vector!(0xCA, 0xFE, 0x01, 0x02);
let decoded = codec.decode(&bytes).unwrap().value;
assert_eq!(decoded, hlist!(1, 2));
}
macro_rules! make_test_hcodec {
{} => {
{
let m = byte_vector!(0xCA, 0xFE);
hcodec!(
{ "magic" => constant(&m) } >>
{ "version" => uint8 } ::
{ "junk_len" => uint8 } >>= |junk_len| { hcodec!(
{ "skip" => ignore(1) } >>
{ "first" => uint8 } ::
{ "junk" => ignore(*junk_len as usize) } >>
{ "second" => uint8 } ::
{ "third" => uint8 }
)}
)
}
};
}
#[test]
fn the_hcodec_macro_should_work_with_a_mix_of_operations() {
let codec = make_test_hcodec!();
let input = hlist!(1u8, 3u8, 7u8, 3u8, 1u8);
let expected =
byte_vector!(0xCA, 0xFE, 0x01, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x01);
assert_round_trip(codec, &input, &Some(expected));
}
record_struct!(TestStruct1, byte1: u8, byte2: u8);
#[test]
fn record_structs_should_work() {
let s1 = TestStruct1::from_hlist(hlist!(7u8, 3u8));
assert_eq!(s1.byte1, 7u8);
assert_eq!(s1.byte2, 3u8);
}
#[test]
fn a_struct_codec_should_round_trip() {
let codec = struct_codec!(TestStruct1 from {uint8} :: {uint8});
assert_round_trip(
codec,
&TestStruct1 {
byte1: 7u8,
byte2: 3u8,
},
&Some(byte_vector!(7, 3)),
);
}
#[test]
fn boxed_codecs_should_work() {
let codec = Box::new(struct_codec!(TestStruct1 from {uint8} :: {uint8}));
assert_round_trip(
codec,
&TestStruct1 {
byte1: 7u8,
byte2: 3u8,
},
&Some(byte_vector!(7, 3)),
);
}
const TEST_CODEC: &'static dyn Codec<Value = i32> = int32;
#[test]
fn static_codecs_should_work() {
assert_round_trip(
TEST_CODEC,
&0x1234_5678,
&Some(byte_vector!(0x12, 0x34, 0x56, 0x78)),
);
}
}