tarrasque 0.10.0

A library for zero-allocation parsing of binary formats.
Documentation
// Copyright 2018 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Big-endian and little-endian number extraction.

#![allow(clippy::many_single_char_names)]

use core::mem::{size_of};

use super::{Extract, ExtractResult, Span, Stream};

/// Returns the first two bytes in the supplied slice as a big-endian `u16`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_u16};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(be_u16(bytes), 0x0102);
/// ```
#[inline]
pub fn be_u16(bytes: &[u8]) -> u16 {
    assert!(bytes.len() >= 2);
    let a = u16::from(bytes[0]) << 8;
    let b = u16::from(bytes[1]);
    a + b
}

/// Returns the first four bytes in the supplied slice as a big-endian `u32`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_u32};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(be_u32(bytes), 0x01020304);
/// ```
#[inline]
pub fn be_u32(bytes: &[u8]) -> u32 {
    assert!(bytes.len() >= 4);
    let a = u32::from(bytes[0]) << 24;
    let b = u32::from(bytes[1]) << 16;
    let c = u32::from(bytes[2]) << 8;
    let d = u32::from(bytes[3]);
    a + b + c + d
}

/// Returns the first eight bytes in the supplied slice as a big-endian `u64`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_u64};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(be_u64(bytes), 0x0102030405060708);
/// ```
#[inline]
pub fn be_u64(bytes: &[u8]) -> u64 {
    assert!(bytes.len() >= 8);
    let a = u64::from(bytes[0]) << 56;
    let b = u64::from(bytes[1]) << 48;
    let c = u64::from(bytes[2]) << 40;
    let d = u64::from(bytes[3]) << 32;
    let e = u64::from(bytes[4]) << 24;
    let f = u64::from(bytes[5]) << 16;
    let g = u64::from(bytes[6]) << 8;
    let h = u64::from(bytes[7]);
    a + b + c + d + e + f + g + h
}

/// Returns the first two bytes in the supplied slice as a big-endian `i16`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_i16};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(be_i16(bytes), 0x0102);
/// ```
#[inline]
pub fn be_i16(bytes: &[u8]) -> i16 {
    be_u16(bytes) as i16
}

/// Returns the first four bytes in the supplied slice as a big-endian `i32`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_i32};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(be_i32(bytes), 0x01020304);
/// ```
#[inline]
pub fn be_i32(bytes: &[u8]) -> i32 {
    be_u32(bytes) as i32
}

/// Returns the first eight bytes in the supplied slice as a big-endian `i64`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_i64};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(be_i64(bytes), 0x0102030405060708);
/// ```
#[inline]
pub fn be_i64(bytes: &[u8]) -> i64 {
    be_u64(bytes) as i64
}

/// Returns the first two bytes in the supplied slice as a little-endian `u16`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_u16};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(le_u16(bytes), 0x0201);
/// ```
#[inline]
pub fn le_u16(bytes: &[u8]) -> u16 {
    assert!(bytes.len() >= 2);
    let a = u16::from(bytes[0]);
    let b = u16::from(bytes[1]) << 8;
    a + b
}

/// Returns the first four bytes in the supplied slice as a little-endian `u32`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_u32};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(le_u32(bytes), 0x04030201);
/// ```
#[inline]
pub fn le_u32(bytes: &[u8]) -> u32 {
    assert!(bytes.len() >= 4);
    let a = u32::from(bytes[0]);
    let b = u32::from(bytes[1]) << 8;
    let c = u32::from(bytes[2]) << 16;
    let d = u32::from(bytes[3]) << 24;
    a + b + c + d
}

/// Returns the first eight bytes in the supplied slice as a little-endian `u64`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_u64};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(le_u64(bytes), 0x0807060504030201);
/// ```
#[inline]
pub fn le_u64(bytes: &[u8]) -> u64 {
    assert!(bytes.len() >= 8);
    let a = u64::from(bytes[0]);
    let b = u64::from(bytes[1]) << 8;
    let c = u64::from(bytes[2]) << 16;
    let d = u64::from(bytes[3]) << 24;
    let e = u64::from(bytes[4]) << 32;
    let f = u64::from(bytes[5]) << 40;
    let g = u64::from(bytes[6]) << 48;
    let h = u64::from(bytes[7]) << 56;
    a + b + c + d + e + f + g + h
}

/// Returns the first two bytes in the supplied slice as a little-endian `i16`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_i16};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(le_i16(bytes), 0x0201);
/// ```
#[inline]
pub fn le_i16(bytes: &[u8]) -> i16 {
    le_u16(bytes) as i16
}

/// Returns the first four bytes in the supplied slice as a little-endian `i32`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_i32};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(le_i32(bytes), 0x04030201);
/// ```
#[inline]
pub fn le_i32(bytes: &[u8]) -> i32 {
    le_u32(bytes) as i32
}

/// Returns the first eight bytes in the supplied slice as a little-endian `i64`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_i64};
/// let bytes = &[1, 2, 3, 4, 5, 6, 7, 8];
/// assert_eq!(le_i64(bytes), 0x0807060504030201);
/// ```
#[inline]
pub fn le_i64(bytes: &[u8]) -> i64 {
    le_u64(bytes) as i64
}

/// Returns the first four bytes in the supplied slice as a big-endian `f32`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_f32};
/// let bytes = &[64, 73, 15, 219];
/// assert_eq!(be_f32(bytes), std::f32::consts::PI);
/// ```
#[inline]
pub fn be_f32(bytes: &[u8]) -> f32 {
    f32::from_bits(be_u32(bytes))
}

/// Returns the first eight bytes in the supplied slice as a big-endian `f64`.
///
/// # Example
///
/// ```
/// # use tarrasque::{be_f64};
/// let bytes = &[64, 9, 33, 251, 84, 68, 45, 24];
/// assert_eq!(be_f64(bytes), std::f64::consts::PI);
/// ```
#[inline]
pub fn be_f64(bytes: &[u8]) -> f64 {
    f64::from_bits(be_u64(bytes))
}

/// Returns the first four bytes in the supplied slice as a little-endian `f32`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_f32};
/// let bytes = &[219, 15, 73, 64];
/// assert_eq!(le_f32(bytes), std::f32::consts::PI);
/// ```
#[inline]
pub fn le_f32(bytes: &[u8]) -> f32 {
    f32::from_bits(le_u32(bytes))
}

/// Returns the first eight bytes in the supplied slice as a little-endian `f64`.
///
/// # Example
///
/// ```
/// # use tarrasque::{le_f64};
/// let bytes = &[24, 45, 68, 84, 251, 33, 9, 64];
/// assert_eq!(le_f64(bytes), std::f64::consts::PI);
/// ```
#[inline]
pub fn le_f64(bytes: &[u8]) -> f64 {
    f64::from_bits(le_u64(bytes))
}

impl<'s> Extract<'s, ()> for u8 {
    #[inline]
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        stream.extract(1).map(|i: &'s [u8]| i[0])
    }
}

impl<'s> Span for u8 {
    const SPAN: usize = 1;
}

impl<'s> Extract<'s, ()> for i8 {
    #[inline]
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        stream.extract(()).map(|i: u8| i as i8)
    }
}

impl<'s> Span for i8 {
    const SPAN: usize = 1;
}

/// A byte order.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Endianness {
    /// The big-endian byte order.
    Big,
    /// The little-endian byte order.
    Little,
}

macro_rules! extract {
    ($type:ty, $big:ident, $little:ident) => {
        impl<'s> Extract<'s, ()> for $type {
            #[inline]
            fn extract(
                stream: &mut Stream<'s>, _: ()
            ) -> ExtractResult<'s, Self> {
                stream.extract(size_of::<$type>()).map($big)
            }
        }

        impl<'s> Extract<'s, Endianness> for $type {
            #[inline]
            fn extract(
                stream: &mut Stream<'s>, endianness: Endianness
            ) -> ExtractResult<'s, Self> {
                let slice = stream.extract(size_of::<$type>())?;
                match endianness {
                    Endianness::Big => Ok($big(slice)),
                    Endianness::Little => Ok($little(slice)),
                }
            }
        }

        impl Span for $type {
            const SPAN: usize = size_of::<$type>();
        }
    };
}

extract!(u16, be_u16, le_u16);
extract!(u32, be_u32, le_u32);
extract!(u64, be_u64, le_u64);
extract!(i16, be_i16, le_i16);
extract!(i32, be_i32, le_i32);
extract!(i64, be_i64, le_i64);
extract!(f32, be_f32, le_f32);
extract!(f64, be_f64, le_f64);

#[cfg(test)]
mod test {
    use super::*;

    use super::Endianness::*;
    use super::super::ExtractError::*;

    #[test]
    fn test_extract_u16() {
        let mut stream = Stream(&[1, 2, 3, 4]);
        assert_eq!(stream.extract::<u16, _>(()), Ok(0x0102));
        assert_eq!(stream.extract::<u16, _>(()), Ok(0x0304));
        assert_eq!(stream.extract::<u16, _>(()), Err(Insufficient(2)));

        let mut stream = Stream(&[1, 2, 3, 4]);
        assert_eq!(stream.extract::<u16, _>(Big), Ok(0x0102));
        assert_eq!(stream.extract::<u16, _>(Big), Ok(0x0304));
        assert_eq!(stream.extract::<u16, _>(Big), Err(Insufficient(2)));

        let mut stream = Stream(&[1, 2, 3, 4]);
        assert_eq!(stream.extract::<u16, _>(Little), Ok(0x0201));
        assert_eq!(stream.extract::<u16, _>(Little), Ok(0x0403));
        assert_eq!(stream.extract::<u16, _>(Little), Err(Insufficient(2)));
    }
}