small_len 1.1.2

A small library for storing the length in the smallest internal type.
Documentation
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);
    }
}