use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
mod external;
mod from;
mod macros;
mod length;
pub use length::{Length, SmallLength};
pub fn length_from_bytes(bytes: &[u8]) -> SmallLength {
assert!(!bytes.is_empty(), "cannot convert empty bytes to small_length");
match bytes {
[253, rest @ ..] => {
assert_eq!(rest.len(), 2, "Insufficient bytes for u16");
SmallLength::Word(u16::from_be_bytes([rest[0], rest[1]]))
},
[254, rest @ ..] => {
assert_eq!(rest.len(), 4, "Insufficient bytes for u32");
SmallLength::Double(u32::from_be_bytes([rest[0], rest[1], rest[2], rest[3]]))
},
[255, rest @ ..] => {
assert_eq!(rest.len(), 4, "Insufficient bytes for u64");
SmallLength::Quad(u64::from_be_bytes([rest[0], rest[1], rest[2], rest[3], rest[4], rest[5], rest[6], rest[7]]))
},
[value, ..] => SmallLength::Byte(*value),
_ => panic!("Unexpected byte format"),
}
}
pub fn length_to_bytes(length: &SmallLength) -> Vec<u8> {
match *length {
SmallLength::Byte(u8) => {
if u8 <= 252 {
u8.to_be_bytes().to_vec()
} else {
let mut bytes = vec![253, 0, 0];
let [a, b] = (u8 as u16).to_be_bytes();
bytes[1] = a;
bytes[2] = b;
bytes
}
},
SmallLength::Word(u) => {
let mut result = vec![253];
result.extend_from_slice(&u.to_be_bytes());
result
},
SmallLength::Double(u) => {
let mut result = vec![254];
result.extend_from_slice(&u.to_be_bytes());
result
},
SmallLength::Quad(u) => {
let mut result = vec![255];
result.extend_from_slice(&u.to_be_bytes());
result
}
}
}
pub trait SmallLen: Len {
#[inline]
fn small_len(&self) -> SmallLength {
self.length().into()
}
}
impl <T> SmallLen for T where T: Len {}
pub trait Len {
fn length(&self) -> Length;
}
impl_len!(String OsString);
impl_len_lifetime!(&'l str, &'l OsStr);
impl_len_generic!([T] &[T] Vec<T>);
impl_len_kv!(HashMap<K, V>);
#[cfg(test)]
mod small_len_tests {
use super::*;
macro_rules! to_from_tests {
($($name:ident($ty:ty): $to:ident, $from:ident),*) => {
$(
#[test]
fn $name() {
let value: $ty = rand::random();
let expected: SmallLength = value.into();
let length: SmallLength = value.into();
let bytes = length.$to();
let result = SmallLength::$from(bytes);
assert_eq!(expected, result);
}
)*
};
}
macro_rules! test_ops {
($($name:ident($op:tt)),*) => {
$(
#[test]
fn $name() {
let lhs: u32 = rand::random();
let rhs: u8 = rand::random();
let rhs = rhs as usize;
let expected = (lhs as usize) $op rhs;
let lhs: SmallLength = lhs.into();
let result: usize = (lhs $op rhs).into();
assert_eq!(result, expected);
}
)*
};
}
to_from_tests! {
byte(u8): to_be_bytes, from_be_bytes,
word(u16): to_be_bytes, from_be_bytes,
double(u32): to_be_bytes, from_be_bytes,
quad(u64): to_be_bytes, from_be_bytes,
usize(usize): to_le_bytes, from_le_bytes
}
test_ops! {
add(+),
sub(-),
mul(*),
div(/),
rem(%)
}
#[test]
fn it_works() {
let result = vec![1, 2, 3].small_len();
assert_eq!(result, SmallLength::Byte(3));
}
#[test]
fn index() {
let result = vec![1, 2, 3];
assert_eq!(result[SmallLength::Byte(0)], 1);
assert_eq!(result[SmallLength::Word(1)], 2);
assert_eq!(result[SmallLength::Double(2)], 3);
}
#[test]
#[should_panic]
fn index_out_of_bounds() {
let result = vec![1, 2, 3];
assert_eq!(result[SmallLength::Byte(5)], 1);
}
}