Maraiah/source/durandal/bin.rs

515 lines
14 KiB
Rust
Raw Normal View History

2018-09-06 09:01:52 -07:00
//! Binary data conversion utilities.
2019-03-01 20:04:20 -08:00
use crate::durandal::{err::*, text::mac_roman_conv};
2019-02-18 20:06:34 -08:00
use std::{fmt, num::NonZeroU16};
#[doc(hidden)]
2019-03-01 01:27:14 -08:00
#[macro_export]
macro_rules! _durandal_read_impl {
2019-02-18 20:06:34 -08:00
// big endian
2019-03-12 13:28:24 -07:00
(BE $b:expr; $nam:ident u16 $n:expr) => {
_durandal_read_impl!($b; u16::from_be_bytes, $nam 2 $n);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
(BE $b:expr; $nam:ident i16 $n:expr) => {
_durandal_read_impl!($b; i16::from_be_bytes, $nam 2 $n);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
(BE $b:expr; $nam:ident u32 $n:expr) => {
_durandal_read_impl!($b; u32::from_be_bytes, $nam 4 $n);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
(BE $b:expr; $nam:ident i32 $n:expr) => {
_durandal_read_impl!($b; i32::from_be_bytes, $nam 4 $n);
2019-02-18 20:06:34 -08:00
};
// little endian
2019-03-12 13:28:24 -07:00
(LE $b:expr; $nam:ident u16 $n:expr) => {
_durandal_read_impl!($b; u16::from_le_bytes, $nam 2 $n);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
(LE $b:expr; $nam:ident i16 $n:expr) => {
_durandal_read_impl!($b; i16::from_le_bytes, $nam 2 $n);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
(LE $b:expr; $nam:ident u32 $n:expr) => {
_durandal_read_impl!($b; u32::from_le_bytes, $nam 4 $n);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
(LE $b:expr; $nam:ident i32 $n:expr) => {
_durandal_read_impl!($b; i32::from_le_bytes, $nam 4 $n);
2019-02-18 20:06:34 -08:00
};
2019-03-01 01:27:14 -08:00
// either endianness
2019-03-12 13:28:24 -07:00
($e:ident $b:expr; $nam:ident Angle $n:expr) => {
_durandal_read_impl!($e $b; $nam u16 $n);
2019-03-01 01:27:14 -08:00
let $nam = Angle::from_bits($nam);
};
2019-03-12 13:28:24 -07:00
($e:ident $b:expr; $nam:ident Fixed $n:expr) => {
_durandal_read_impl!($e $b; $nam u32 $n);
2019-03-01 01:27:14 -08:00
let $nam = Fixed::from_bits($nam);
};
2019-03-12 13:28:24 -07:00
($e:ident $b:expr; $nam:ident Unit $n:expr) => {
_durandal_read_impl!($e $b; $nam u16 $n);
2019-03-01 01:27:14 -08:00
let $nam = Unit::from_bits($nam);
};
2019-03-12 13:28:24 -07:00
($e:ident $b:expr; $nam:ident OptU16 $n:expr) => {
_durandal_read_impl!($e $b; $nam u16 $n);
2019-03-18 09:07:44 -07:00
let $nam = OptU16::from($nam);
2019-03-01 01:27:14 -08:00
};
2019-03-12 13:28:24 -07:00
($e:ident $b:expr; $nam:ident usize u16 $n:expr) => {
_durandal_read_impl!($e $b; $nam u16 $n);
2019-03-02 18:31:00 -08:00
let $nam = usize::from($nam);
};
2019-03-12 13:28:24 -07:00
($e:ident $b:expr; $nam:ident usize u32 $n:expr) => {
_durandal_read_impl!($e $b; $nam u32 $n);
2019-03-02 18:31:00 -08:00
let $nam = usize_from_u32($nam);
};
2019-03-01 01:27:14 -08:00
2019-03-02 18:31:00 -08:00
// no endianness
2019-03-12 13:28:24 -07:00
($_:ident $b:expr; $nam:ident u8 $n:expr) => {let $nam = $b[$n];};
($_:ident $b:expr; $nam:ident slice u8 $n:expr) => {let $nam = &$b[$n];};
($_:ident $b:expr; $nam:ident i8 $n:expr) => {
2019-03-02 18:31:00 -08:00
let $nam = i8::from_ne_bytes([$b[$n]]);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
($_:ident $b:expr; $nam:ident Ident $n:expr) => {
2019-03-01 20:04:20 -08:00
let $nam = Ident([$b[$n], $b[$n + 1], $b[$n + 2], $b[$n + 3]]);
2019-02-18 20:06:34 -08:00
};
2019-03-12 13:28:24 -07:00
($_:ident $b:expr; $nam:ident $f:ident $n:expr) => {
2019-02-18 20:06:34 -08:00
let $nam = $f(&$b[$n])?;
};
2019-03-12 13:28:24 -07:00
($_:ident $b:expr; $nam:ident no_try $f:ident $n:expr) => {
2019-02-18 20:06:34 -08:00
let $nam = $f(&$b[$n]);
};
// worker - creates let statement
2019-03-12 13:28:24 -07:00
($b:expr; $pth:path , $nam:ident 2 $n:expr) => {
2019-02-18 20:06:34 -08:00
let $nam = $pth([$b[$n], $b[$n + 1]]);
};
2019-03-12 13:28:24 -07:00
($b:expr; $pth:path , $nam:ident 4 $n:expr) => {
2019-02-18 20:06:34 -08:00
let $nam = $pth([$b[$n], $b[$n + 1], $b[$n + 2], $b[$n + 3]]);
};
2019-02-10 20:29:06 -08:00
}
2019-02-09 21:52:23 -08:00
2019-03-02 18:31:00 -08:00
/// Reads structured data from a byte slice.
2019-03-01 01:27:14 -08:00
///
2019-03-04 18:14:09 -08:00
/// # Syntax
///
2019-03-01 01:27:14 -08:00
/// First start by specifying the endianness, size and source using the syntax
/// `endian, size in source =>` where:
///
/// - `endian` is `BE` or `LE` for big- or little-endian respectively.
/// - `size` is an expression specifying the last index that should be used by
/// this macro in `source`.
/// - `source` is a `u8` slice to read data from.
///
/// After the initializer line, all lines have the syntax
/// `name = type[place] opts;` where:
///
/// - `name` is the binding to put the resulting data in.
/// - `type` is one of:
/// - `u8` or `i8`: one byte will be read at `place`.
/// - `u16` or `i16`: two bytes will be read at `place` with `from_*_bytes`.
/// - `u32` or `i32`: four bytes will be read at `place` with `from_*_bytes`.
/// - `Ident`: four bytes will be read at `place` into an array, disregarding
2019-03-04 02:18:57 -08:00
/// endianness, creating an `Ident` object.
2019-03-01 01:27:14 -08:00
/// - `Angle`: same as `u16`, but the result is passed to
2019-03-04 02:18:57 -08:00
/// `fixed::Angle::from_bits`, resulting in a `fixed::Angle` object.
2019-03-01 01:27:14 -08:00
/// - `Fixed`: same as `u32`, but the result is passed to
2019-03-04 02:18:57 -08:00
/// `fixed::Fixed::from_bits`, resulting in a `fixed::Fixed` object.
2019-03-01 01:27:14 -08:00
/// - `Unit`: same as `u16`, but the result is passed to
2019-03-04 02:18:57 -08:00
/// `fixed::Unit::from_bits`, resulting in a `fixed::Unit` object.
2019-03-01 01:27:14 -08:00
/// - `OptU16`: same as `u16`, but the result is passed to
2019-03-18 09:07:44 -07:00
/// `OptU16::from`, resulting in an `OptU16` object.
2019-03-01 01:27:14 -08:00
/// - The name of a function, which is passed `&source[place]` as its only
2019-03-04 02:18:57 -08:00
/// argument. The function's result has the `?` operator applied to it.
2019-03-01 01:27:14 -08:00
/// - `opts` may be one of:
2019-03-02 18:31:00 -08:00
/// - `slice` when `type` is `u8`: `place` is a range specifying a `u8` slice
2019-03-04 02:18:57 -08:00
/// to be taken from `source`.
/// - `usize` when `type` is `u16` or `u32`: converts the resulting integer to
/// `usize` by `usize_to_u32` for `u32` or by `from` for `u16`.
2019-03-02 18:31:00 -08:00
/// - `no_try` when `type` is a function name: does not use the `?` operator
2019-03-04 02:18:57 -08:00
/// on the resulting function call.
2019-03-01 01:27:14 -08:00
/// - Nothing at all.
/// - `place` is either an integer literal which must be representable as
/// `usize`, or a range, which may only be used when `type` is a function
/// name.
2019-03-04 18:14:09 -08:00
///
/// # Panics
///
2019-03-08 08:39:21 -08:00
/// This macro will not panic unless any index expression used exceeds or
/// equals `size`.
2019-03-04 18:14:09 -08:00
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate maraiah;
/// # use maraiah::durandal::err::*;
/// # fn main() -> ResultS<()>
/// # {
/// let buffer = &[4, 0, 2, 0, 0, 0, 6];
///
/// read_data! {
/// 7, LE in buffer =>
/// four = u16[0];
/// two = u32[2];
/// six = u8 [6];
/// }
///
/// assert_eq!(four, 4_u16);
/// assert_eq!(two, 2_u32);
/// assert_eq!(six, 6_u8);
/// # Ok(())
/// # }
/// ```
2019-03-09 17:27:19 -08:00
#[macro_export]
2019-02-18 20:06:34 -08:00
macro_rules! read_data {
(
2019-03-12 13:28:24 -07:00
$sz:expr , $ty:ident in $b:expr =>
2019-02-18 20:06:34 -08:00
$( $nam:ident = $t:ident [ $n:expr ] $( $ex:ident )* ; )*
2019-03-09 17:27:19 -08:00
) => {
$crate::check_data!($sz, $b);
2019-03-12 13:28:24 -07:00
$($crate::_durandal_read_impl!($ty $b; $nam $($ex)* $t $n);)*
2019-03-09 17:27:19 -08:00
};
}
/// Checks if there is enough data in `b`.
#[macro_export]
macro_rules! check_data {
(
2019-03-12 13:28:24 -07:00
$sz:expr , $b:expr
2019-02-18 20:06:34 -08:00
) => {
if $b.len() < $sz {
2019-03-04 18:14:09 -08:00
return Err(err_msg("not enough data"));
2019-02-18 20:06:34 -08:00
}
};
2019-02-10 20:29:06 -08:00
}
2019-02-09 21:52:23 -08:00
2019-03-02 18:31:00 -08:00
/// Casts a `u32` to a `usize`. For future compatibility.
2019-03-04 18:14:09 -08:00
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::usize_from_u32;
///
/// assert_eq!(usize_from_u32(777u32), 777usize);
/// ```
2019-03-02 18:31:00 -08:00
#[inline]
pub const fn usize_from_u32(n: u32) -> usize {n as usize}
2019-03-01 01:27:14 -08:00
/// Creates an `Ident` from a slice.
///
2019-03-04 18:14:09 -08:00
/// # Panics
///
/// A panic will occur if `b.len()` is less than 4.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::{Ident, ident};
///
/// assert_eq!(ident(b"POLY"), Ident([b'P', b'O', b'L', b'Y']));
/// ```
#[inline]
pub const fn ident(b: &[u8]) -> Ident {Ident([b[0], b[1], b[2], b[3]])}
2019-03-01 01:27:14 -08:00
/// Applies `u32::from_be_bytes` to a slice.
///
2019-03-04 18:14:09 -08:00
/// # Panics
///
/// A panic will occur if `b.len()` is less than 4.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::u32b;
///
/// assert_eq!(u32b(&[0x00, 0x0B, 0xDE, 0x31]), 777_777u32);
/// ```
2019-02-20 18:33:57 -08:00
pub fn u32b(b: &[u8]) -> u32 {u32::from_be_bytes([b[0], b[1], b[2], b[3]])}
2019-03-01 01:27:14 -08:00
/// Applies `u16::from_be_bytes` to a slice.
///
2019-03-04 18:14:09 -08:00
/// # Panics
///
/// A panic will occur if `b.len()` is less than 2.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::u16b;
///
/// assert_eq!(u16b(&[0x1E, 0x61]), 7_777u16);
/// ```
2019-02-20 18:33:57 -08:00
pub fn u16b(b: &[u8]) -> u16 {u16::from_be_bytes([b[0], b[1]])}
2019-03-01 01:27:14 -08:00
/// Applies `i32::from_be_bytes` to a slice.
///
2019-03-04 18:14:09 -08:00
/// # Panics
///
/// A panic will occur if `b.len()` is less than 4.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::i32b;
///
/// assert_eq!(i32b(&[0xFF, 0x89, 0x52, 0x0F]), -7_777_777i32);
/// ```
2019-02-20 18:33:57 -08:00
pub fn i32b(b: &[u8]) -> i32 {i32::from_be_bytes([b[0], b[1], b[2], b[3]])}
2019-03-01 01:27:14 -08:00
/// Applies `i16::from_be_bytes` to a slice.
///
2019-03-04 18:14:09 -08:00
/// # Panics
///
/// A panic will occur if `b.len()` is less than 2.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::i16b;
///
/// assert_eq!(i16b(&[0xE1, 0x9F]), -7_777i16);
/// ```
2019-02-20 18:33:57 -08:00
pub fn i16b(b: &[u8]) -> i16 {i16::from_be_bytes([b[0], b[1]])}
2018-09-06 09:01:52 -07:00
2019-03-01 01:27:14 -08:00
/// Applies a read function over a slice.
///
/// Applies `read` over `b`, resulting in a vector of its return values. Each
/// iteration will pass a slice of `b` to `read` for it to read from, and then
/// increments the slice index by the second return value. When there is no
/// data left in `b`, the function returns.
2019-03-04 18:14:09 -08:00
///
/// # Panics
///
/// A panic will occur if the `read` function returns a disjoint index or
/// otherwise panics (by an out of bounds index to `b` or otherwise.)
///
/// # Errors
///
/// Execution will return the result of `read` if `read` returns an error.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::{err::*, bin::{rd_array, u16b}};
///
/// fn read_a_u16(b: &[u8]) -> ResultS<(u16, usize)> {Ok((u16b(b), 2))}
///
/// let inp = &[0x1E, 0x61, 0x03, 0x09];
/// assert_eq!(rd_array(inp, read_a_u16).unwrap(), vec![7_777u16, 777u16]);
/// ```
2019-02-18 20:06:34 -08:00
pub fn rd_array<T, F>(b: &[u8], read: F) -> ResultS<Vec<T>>
2019-02-18 08:52:18 -08:00
where T: Sized,
F: Fn(&[u8]) -> ResultS<(T, usize)>
2019-02-10 20:29:06 -08:00
{
2019-02-18 08:52:18 -08:00
let mut v = Vec::new();
let mut p = 0;
while p < b.len() {
2019-02-18 08:52:18 -08:00
let (r, s) = read(&b[p..])?;
v.push(r);
p += s;
2019-02-09 11:01:35 -08:00
}
Ok(v)
}
2019-03-04 20:57:25 -08:00
/// Applies a read function a number of times over a slice.
///
/// Applies `read` over `b`, resulting in a vector of its return values. Each
/// iteration will pass a slice of `b` to `read` for it to read from, and then
/// increments the slice index by the second return value. When `n` elements
/// have been read, the function returns.
///
/// # Panics
///
/// A panic will occur if the `read` function returns a disjoint index or
/// otherwise panics (by an out of bounds index to `b` or otherwise.)
///
/// # Errors
///
/// Execution will return the result of `read` if `read` returns an error.
pub fn rd_array_num<T, F>(b: &[u8], n: usize, read: F)
-> ResultS<(Vec<T>, usize)>
where T: Sized,
F: Fn(&[u8]) -> ResultS<(T, usize)>
{
let mut v = Vec::with_capacity(n);
let mut p = 0;
for _ in 0..n {
let (r, s) = read(&b[p..])?;
v.push(r);
p += s;
}
Ok((v, p))
}
2019-03-01 01:27:14 -08:00
/// Applies a read function over a slice with an offset table.
///
/// Applies `read` over each offset in `b`, of which there are `num` amount of
/// starting at `p`, resulting in a vector of its return values. Each iteration
/// reads a 32-bit big endian offset from `b`, and then passes a slice of `b`
/// to `read` starting at that offset. When all offsets have been read, the
/// function returns.
2019-03-04 18:14:09 -08:00
///
/// # Panics
///
/// A panic will occur if the `read` function panics.
2019-03-04 18:14:09 -08:00
///
/// # Errors
///
/// Execution will return the result of `read` if `read` returns an error.
2019-02-18 20:06:34 -08:00
pub fn rd_ofstable<T, F>(b: &[u8],
2019-02-20 18:33:57 -08:00
mut p: usize,
num: usize,
read: F)
2019-03-02 21:44:45 -08:00
-> ResultS<Vec<T>>
2019-02-18 20:06:34 -08:00
where T: Sized,
F: Fn(&[u8]) -> ResultS<T>
{
let mut v = Vec::with_capacity(num);
for _ in 0..num {
2019-03-02 18:31:00 -08:00
let ofs = usize_from_u32(u32b(&b[p..p + 4]));
2019-03-09 17:27:19 -08:00
check_data!(ofs, b);
2019-02-18 20:06:34 -08:00
v.push(read(&b[ofs..])?);
p += 4;
}
Ok(v)
}
2019-03-18 09:07:44 -07:00
impl From<u16> for OptU16
2019-02-09 11:01:35 -08:00
{
2019-03-18 09:07:44 -07:00
#[inline]
fn from(n: u16) -> Self
2019-02-09 11:01:35 -08:00
{
if n == u16::max_value() {
2019-02-24 20:34:59 -08:00
Self(None)
2019-02-09 11:01:35 -08:00
} else {
2019-02-24 20:34:59 -08:00
Self(NonZeroU16::new(n + 1))
2019-02-09 11:01:35 -08:00
}
}
2019-03-18 09:07:44 -07:00
}
2019-02-09 11:01:35 -08:00
2019-03-18 09:07:44 -07:00
impl Into<u16> for OptU16
{
2019-03-01 01:27:14 -08:00
/// Returns the `u16` representation.
2019-03-04 18:14:09 -08:00
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::OptU16;
///
2019-03-18 09:07:44 -07:00
/// let u16_max = u16::max_value();
2019-03-04 18:14:09 -08:00
///
2019-03-18 09:07:44 -07:00
/// // These type annotations are necessary.
///
/// assert_eq!(<OptU16 as Into<u16>>::into(OptU16::from(500u16)), 500u16);
/// assert_eq!(<OptU16 as Into<u16>>::into(OptU16::from(u16_max)), u16_max);
/// assert_eq!(<OptU16 as Into<u16>>::into(OptU16::from(0u16)), 0u16);
2019-03-04 18:14:09 -08:00
/// ```
2019-03-18 09:07:44 -07:00
#[inline]
fn into(self) -> u16
2019-02-09 11:01:35 -08:00
{
match self.0 {
2019-03-04 02:18:57 -08:00
None => u16::max_value(),
2019-02-09 11:01:35 -08:00
Some(n) => n.get() - 1,
}
}
2019-03-18 09:07:44 -07:00
}
impl OptU16
{
/// Creates an `OptU16` representing `None`.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::OptU16;
///
/// assert_eq!(OptU16::none(), OptU16::from(u16::max_value()));
/// ```
#[inline]
pub const fn none() -> Self {Self(None)}
2019-02-09 11:01:35 -08:00
2019-03-01 01:27:14 -08:00
/// Returns the `Option` representation.
2019-03-04 18:14:09 -08:00
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::OptU16;
///
2019-03-18 09:07:44 -07:00
/// assert_eq!(OptU16::from(500u16).get(), Some(500u16));
/// assert_eq!(OptU16::from(u16::max_value()).get(), None);
/// assert_eq!(OptU16::from(0u16).get(), Some(0u16));
2019-03-04 18:14:09 -08:00
/// ```
2019-03-18 09:07:44 -07:00
#[inline]
2019-03-09 14:48:55 -08:00
pub fn get(self) -> Option<u16>
2019-02-09 11:01:35 -08:00
{
match self.0 {
2019-03-04 02:18:57 -08:00
None => None,
2019-02-17 18:16:01 -08:00
Some(n) => Some(n.get() - 1),
2019-02-09 11:01:35 -08:00
}
}
}
2019-02-21 14:11:48 -08:00
impl fmt::Debug for OptU16
2019-02-10 02:31:57 -08:00
{
2019-02-24 20:34:59 -08:00
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
2019-02-10 02:31:57 -08:00
{
match self.get() {
2019-03-01 03:22:27 -08:00
None => write!(f, "None"),
2019-02-21 11:35:16 -08:00
Some(n) => write!(f, "Some({})", n),
2019-02-10 02:31:57 -08:00
}
}
}
2019-03-01 20:04:20 -08:00
impl fmt::Debug for Ident
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "\"{}\"", mac_roman_conv(&self.0))
}
}
2019-03-13 07:53:30 -07:00
impl PartialEq<[u8; 4]> for Ident
{
#[inline]
fn eq(&self, o: &[u8; 4]) -> bool {self.0 == *o}
}
impl<'a> PartialEq<[u8; 4]> for &'a Ident
{
#[inline]
fn eq(&self, o: &[u8; 4]) -> bool {PartialEq::eq(*self, o)}
}
impl<'a> PartialEq<&'a [u8; 4]> for Ident
{
#[inline]
fn eq(&self, o: & &'a [u8; 4]) -> bool {PartialEq::eq(self, *o)}
}
2019-02-10 02:31:57 -08:00
/// A four-character-code identifier.
2019-03-04 18:14:09 -08:00
///
/// # Examples
///
/// ```
/// use maraiah::durandal::bin::Ident;
///
2019-03-13 07:53:30 -07:00
/// assert_eq!( Ident(*b"POLY").0, *b"POLY");
/// assert_eq!( Ident(*b"POLY"), *b"POLY");
/// assert_eq!( Ident(*b"POLY"), b"POLY");
/// assert_eq!(&Ident(*b"POLY"), *b"POLY");
/// assert_eq!(&Ident(*b"POLY"), b"POLY");
2019-03-04 18:14:09 -08:00
/// ```
2019-03-13 07:53:30 -07:00
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize, serde::Deserialize))]
2019-03-09 14:03:20 -08:00
pub struct Ident(/** The individual bytes of this identifier. */ pub [u8; 4]);
2019-02-10 02:31:57 -08:00
/// An object identified by a `u16` which may be `u16::max_value()` to
2019-03-01 01:27:14 -08:00
/// represent a nulled value.
2019-03-13 07:53:30 -07:00
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize, serde::Deserialize))]
2019-02-21 14:11:48 -08:00
pub struct OptU16(Option<NonZeroU16>);
2019-02-10 02:31:57 -08:00
2018-09-06 09:01:52 -07:00
// EOF