#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PerfByteReader {
source_big_endian: bool,
}
impl PerfByteReader {
pub const HOST_IS_BIG_ENDIAN: bool = cfg!(target_endian = "big");
pub const KEEP_ENDIAN: Self = Self::new(Self::HOST_IS_BIG_ENDIAN);
pub const SWAP_ENDIAN: Self = Self::new(!Self::HOST_IS_BIG_ENDIAN);
pub const fn new(source_big_endian: bool) -> Self {
return Self { source_big_endian };
}
pub const fn source_big_endian(self) -> bool {
return self.source_big_endian;
}
pub const fn byte_swap_needed(self) -> bool {
return self.source_big_endian != Self::HOST_IS_BIG_ENDIAN;
}
pub fn read_i16(self, source: &[u8]) -> i16 {
debug_assert!(source.len() >= 2, "source.len() >= 2");
let source_array = source[..2].try_into().unwrap();
return if self.source_big_endian {
i16::from_be_bytes(source_array)
} else {
i16::from_le_bytes(source_array)
};
}
pub fn read_u16(self, source: &[u8]) -> u16 {
debug_assert!(source.len() >= 2, "source.len() >= 2");
let source_array = source[..2].try_into().unwrap();
return if self.source_big_endian {
u16::from_be_bytes(source_array)
} else {
u16::from_le_bytes(source_array)
};
}
pub fn read_i32(self, source: &[u8]) -> i32 {
debug_assert!(source.len() >= 4, "source.len() >= 4");
let source_array = source[..4].try_into().unwrap();
return if self.source_big_endian {
i32::from_be_bytes(source_array)
} else {
i32::from_le_bytes(source_array)
};
}
pub fn read_u32(self, source: &[u8]) -> u32 {
debug_assert!(source.len() >= 4, "source.len() >= 4");
let source_array = source[..4].try_into().unwrap();
return if self.source_big_endian {
u32::from_be_bytes(source_array)
} else {
u32::from_le_bytes(source_array)
};
}
pub fn read_i64(self, source: &[u8]) -> i64 {
debug_assert!(source.len() >= 8, "source.len() >= 8");
let source_array = source[..8].try_into().unwrap();
return if self.source_big_endian {
i64::from_be_bytes(source_array)
} else {
i64::from_le_bytes(source_array)
};
}
pub fn read_u64(self, source: &[u8]) -> u64 {
debug_assert!(source.len() >= 8, "source.len() >= 8");
let source_array = source[..8].try_into().unwrap();
return if self.source_big_endian {
u64::from_be_bytes(source_array)
} else {
u64::from_le_bytes(source_array)
};
}
pub fn read_f32(self, source: &[u8]) -> f32 {
debug_assert!(source.len() >= 4, "source.len() >= 4");
let source_array = source[..4].try_into().unwrap();
return if self.source_big_endian {
f32::from_be_bytes(source_array)
} else {
f32::from_le_bytes(source_array)
};
}
pub fn read_f64(self, source: &[u8]) -> f64 {
debug_assert!(source.len() >= 8, "source.len() >= 8");
let source_array = source[..8].try_into().unwrap();
return if self.source_big_endian {
f64::from_be_bytes(source_array)
} else {
f64::from_le_bytes(source_array)
};
}
pub const fn fix_u16(self, value: u16) -> u16 {
return if self.source_big_endian == Self::HOST_IS_BIG_ENDIAN {
value
} else {
value.swap_bytes()
};
}
pub const fn fix_u32(self, value: u32) -> u32 {
return if self.source_big_endian == Self::HOST_IS_BIG_ENDIAN {
value
} else {
value.swap_bytes()
};
}
pub const fn fix_u64(self, value: u64) -> u64 {
return if self.source_big_endian == Self::HOST_IS_BIG_ENDIAN {
value
} else {
value.swap_bytes()
};
}
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_I16: i16 = 0x1234;
const TEST_U16: u16 = 0x1234;
const TEST_I32: i32 = 0x12345678;
const TEST_U32: u32 = 0x12345678;
const TEST_I64: i64 = 0x1234567890abcdef;
const TEST_U64: u64 = 0x1234567890abcdef;
const TEST_F32: f32 = 1234.5678;
const TEST_F64: f64 = 1234.5678;
#[test]
fn constants() {
const TARGET_BIG_ENDIAN: bool = TEST_U32.to_be() == TEST_U32;
assert_eq!(TARGET_BIG_ENDIAN, PerfByteReader::HOST_IS_BIG_ENDIAN);
assert_eq!(
TARGET_BIG_ENDIAN,
PerfByteReader::KEEP_ENDIAN.source_big_endian()
);
assert_eq!(
!TARGET_BIG_ENDIAN,
PerfByteReader::SWAP_ENDIAN.source_big_endian()
);
assert_eq!(false, PerfByteReader::new(false).source_big_endian());
assert_eq!(true, PerfByteReader::new(true).source_big_endian());
assert_eq!(false, PerfByteReader::KEEP_ENDIAN.byte_swap_needed());
assert_eq!(true, PerfByteReader::SWAP_ENDIAN.byte_swap_needed());
assert_eq!(
TARGET_BIG_ENDIAN,
PerfByteReader::new(false).byte_swap_needed()
);
assert_eq!(
!TARGET_BIG_ENDIAN,
PerfByteReader::new(true).byte_swap_needed()
);
}
#[test]
fn read() {
assert_eq!(
TEST_I16,
PerfByteReader::new(false).read_i16(&TEST_I16.to_le_bytes())
);
assert_eq!(
TEST_I16,
PerfByteReader::new(true).read_i16(&TEST_I16.to_be_bytes())
);
assert_eq!(
TEST_U16,
PerfByteReader::new(false).read_u16(&TEST_U16.to_le_bytes())
);
assert_eq!(
TEST_U16,
PerfByteReader::new(true).read_u16(&TEST_U16.to_be_bytes())
);
assert_eq!(
TEST_I32,
PerfByteReader::new(false).read_i32(&TEST_I32.to_le_bytes())
);
assert_eq!(
TEST_I32,
PerfByteReader::new(true).read_i32(&TEST_I32.to_be_bytes())
);
assert_eq!(
TEST_U32,
PerfByteReader::new(false).read_u32(&TEST_U32.to_le_bytes())
);
assert_eq!(
TEST_U32,
PerfByteReader::new(true).read_u32(&TEST_U32.to_be_bytes())
);
assert_eq!(
TEST_I64,
PerfByteReader::new(false).read_i64(&TEST_I64.to_le_bytes())
);
assert_eq!(
TEST_I64,
PerfByteReader::new(true).read_i64(&TEST_I64.to_be_bytes())
);
assert_eq!(
TEST_U64,
PerfByteReader::new(false).read_u64(&TEST_U64.to_le_bytes())
);
assert_eq!(
TEST_U64,
PerfByteReader::new(true).read_u64(&TEST_U64.to_be_bytes())
);
assert_eq!(
TEST_F32,
PerfByteReader::new(false).read_f32(&TEST_F32.to_le_bytes())
);
assert_eq!(
TEST_F32,
PerfByteReader::new(true).read_f32(&TEST_F32.to_be_bytes())
);
assert_eq!(
TEST_F64,
PerfByteReader::new(false).read_f64(&TEST_F64.to_le_bytes())
);
assert_eq!(
TEST_F64,
PerfByteReader::new(true).read_f64(&TEST_F64.to_be_bytes())
);
}
#[test]
fn fix() {
assert_eq!(
TEST_U16,
PerfByteReader::new(false).fix_u16(TEST_U16.to_le())
);
assert_eq!(
TEST_U16,
PerfByteReader::new(true).fix_u16(TEST_U16.to_be())
);
assert_eq!(
TEST_U32,
PerfByteReader::new(false).fix_u32(TEST_U32.to_le())
);
assert_eq!(
TEST_U32,
PerfByteReader::new(true).fix_u32(TEST_U32.to_be())
);
assert_eq!(
TEST_U64,
PerfByteReader::new(false).fix_u64(TEST_U64.to_le())
);
assert_eq!(
TEST_U64,
PerfByteReader::new(true).fix_u64(TEST_U64.to_be())
);
}
}