use std::mem;
#[inline]
pub fn byte_swap_16(x: u16) -> u16 {
x.swap_bytes()
}
#[inline]
pub fn byte_swap_32(x: u32) -> u32 {
x.swap_bytes()
}
#[inline]
pub fn byte_swap_64(x: u64) -> u64 {
x.swap_bytes()
}
#[inline]
pub fn hton_16(x: u16) -> u16 {
x.to_be()
}
#[inline]
pub fn hton_32(x: u32) -> u32 {
x.to_be()
}
#[inline]
pub fn hton_64(x: u64) -> u64 {
x.to_be()
}
#[inline]
pub fn ntoh_16(x: u16) -> u16 {
u16::from_be(x)
}
#[inline]
pub fn ntoh_32(x: u32) -> u32 {
u32::from_be(x)
}
#[inline]
pub fn ntoh_64(x: u64) -> u64 {
u64::from_be(x)
}
#[inline]
pub fn htole_16(x: u16) -> u16 {
x.to_le()
}
#[inline]
pub fn htole_32(x: u32) -> u32 {
x.to_le()
}
#[inline]
pub fn htole_64(x: u64) -> u64 {
x.to_le()
}
#[inline]
pub fn letoh_16(x: u16) -> u16 {
u16::from_le(x)
}
#[inline]
pub fn letoh_32(x: u32) -> u32 {
u32::from_le(x)
}
#[inline]
pub fn letoh_64(x: u64) -> u64 {
u64::from_le(x)
}
pub fn read_u16_be(bytes: &[u8], offset: usize) -> Option<u16> {
if offset + 2 <= bytes.len() {
Some(u16::from_be_bytes([bytes[offset], bytes[offset + 1]]))
} else {
None
}
}
pub fn read_u32_be(bytes: &[u8], offset: usize) -> Option<u32> {
if offset + 4 <= bytes.len() {
Some(u32::from_be_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
]))
} else {
None
}
}
pub fn read_u64_be(bytes: &[u8], offset: usize) -> Option<u64> {
if offset + 8 <= bytes.len() {
Some(u64::from_be_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
bytes[offset + 4],
bytes[offset + 5],
bytes[offset + 6],
bytes[offset + 7],
]))
} else {
None
}
}
pub fn read_u16_le(bytes: &[u8], offset: usize) -> Option<u16> {
if offset + 2 <= bytes.len() {
Some(u16::from_le_bytes([bytes[offset], bytes[offset + 1]]))
} else {
None
}
}
pub fn read_u32_le(bytes: &[u8], offset: usize) -> Option<u32> {
if offset + 4 <= bytes.len() {
Some(u32::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
]))
} else {
None
}
}
pub fn read_u64_le(bytes: &[u8], offset: usize) -> Option<u64> {
if offset + 8 <= bytes.len() {
Some(u64::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
bytes[offset + 4],
bytes[offset + 5],
bytes[offset + 6],
bytes[offset + 7],
]))
} else {
None
}
}
pub fn write_u16_be(bytes: &mut [u8], offset: usize, value: u16) -> bool {
if offset + 2 <= bytes.len() {
let be_bytes = value.to_be_bytes();
bytes[offset] = be_bytes[0];
bytes[offset + 1] = be_bytes[1];
true
} else {
false
}
}
pub fn write_u32_be(bytes: &mut [u8], offset: usize, value: u32) -> bool {
if offset + 4 <= bytes.len() {
let be_bytes = value.to_be_bytes();
bytes[offset] = be_bytes[0];
bytes[offset + 1] = be_bytes[1];
bytes[offset + 2] = be_bytes[2];
bytes[offset + 3] = be_bytes[3];
true
} else {
false
}
}
pub fn write_u64_be(bytes: &mut [u8], offset: usize, value: u64) -> bool {
if offset + 8 <= bytes.len() {
let be_bytes = value.to_be_bytes();
bytes[offset] = be_bytes[0];
bytes[offset + 1] = be_bytes[1];
bytes[offset + 2] = be_bytes[2];
bytes[offset + 3] = be_bytes[3];
bytes[offset + 4] = be_bytes[4];
bytes[offset + 5] = be_bytes[5];
bytes[offset + 6] = be_bytes[6];
bytes[offset + 7] = be_bytes[7];
true
} else {
false
}
}
pub fn write_u16_le(bytes: &mut [u8], offset: usize, value: u16) -> bool {
if offset + 2 <= bytes.len() {
let le_bytes = value.to_le_bytes();
bytes[offset] = le_bytes[0];
bytes[offset + 1] = le_bytes[1];
true
} else {
false
}
}
pub fn write_u32_le(bytes: &mut [u8], offset: usize, value: u32) -> bool {
if offset + 4 <= bytes.len() {
let le_bytes = value.to_le_bytes();
bytes[offset] = le_bytes[0];
bytes[offset + 1] = le_bytes[1];
bytes[offset + 2] = le_bytes[2];
bytes[offset + 3] = le_bytes[3];
true
} else {
false
}
}
pub fn write_u64_le(bytes: &mut [u8], offset: usize, value: u64) -> bool {
if offset + 8 <= bytes.len() {
let le_bytes = value.to_le_bytes();
bytes[offset] = le_bytes[0];
bytes[offset + 1] = le_bytes[1];
bytes[offset + 2] = le_bytes[2];
bytes[offset + 3] = le_bytes[3];
bytes[offset + 4] = le_bytes[4];
bytes[offset + 5] = le_bytes[5];
bytes[offset + 6] = le_bytes[6];
bytes[offset + 7] = le_bytes[7];
true
} else {
false
}
}
#[inline]
pub fn is_little_endian() -> bool {
cfg!(target_endian = "little")
}
#[inline]
pub fn is_big_endian() -> bool {
cfg!(target_endian = "big")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Endianness {
Little,
Big,
}
impl Endianness {
pub fn native() -> Self {
if is_little_endian() {
Endianness::Little
} else {
Endianness::Big
}
}
pub fn network() -> Self {
Endianness::Big
}
pub fn is_little(&self) -> bool {
*self == Endianness::Little
}
pub fn is_big(&self) -> bool {
*self == Endianness::Big
}
pub fn read_u16(&self, bytes: &[u8], offset: usize) -> Option<u16> {
match self {
Endianness::Little => read_u16_le(bytes, offset),
Endianness::Big => read_u16_be(bytes, offset),
}
}
pub fn read_u32(&self, bytes: &[u8], offset: usize) -> Option<u32> {
match self {
Endianness::Little => read_u32_le(bytes, offset),
Endianness::Big => read_u32_be(bytes, offset),
}
}
pub fn read_u64(&self, bytes: &[u8], offset: usize) -> Option<u64> {
match self {
Endianness::Little => read_u64_le(bytes, offset),
Endianness::Big => read_u64_be(bytes, offset),
}
}
pub fn write_u16(&self, bytes: &mut [u8], offset: usize, value: u16) -> bool {
match self {
Endianness::Little => write_u16_le(bytes, offset, value),
Endianness::Big => write_u16_be(bytes, offset, value),
}
}
pub fn write_u32(&self, bytes: &mut [u8], offset: usize, value: u32) -> bool {
match self {
Endianness::Little => write_u32_le(bytes, offset, value),
Endianness::Big => write_u32_be(bytes, offset, value),
}
}
pub fn write_u64(&self, bytes: &mut [u8], offset: usize, value: u64) -> bool {
match self {
Endianness::Little => write_u64_le(bytes, offset, value),
Endianness::Big => write_u64_be(bytes, offset, value),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PackedU16<const IS_LE: bool>(u16);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PackedU32<const IS_LE: bool>(u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PackedU64<const IS_LE: bool>(u64);
impl<const IS_LE: bool> PackedU16<IS_LE> {
pub fn new(value: u16) -> Self {
if IS_LE {
Self(value.to_le())
} else {
Self(value.to_be())
}
}
pub fn get(&self) -> u16 {
if IS_LE {
u16::from_le(self.0)
} else {
u16::from_be(self.0)
}
}
pub fn set(&mut self, value: u16) {
*self = Self::new(value);
}
}
impl<const IS_LE: bool> PackedU32<IS_LE> {
pub fn new(value: u32) -> Self {
if IS_LE {
Self(value.to_le())
} else {
Self(value.to_be())
}
}
pub fn get(&self) -> u32 {
if IS_LE {
u32::from_le(self.0)
} else {
u32::from_be(self.0)
}
}
pub fn set(&mut self, value: u32) {
*self = Self::new(value);
}
}
impl<const IS_LE: bool> PackedU64<IS_LE> {
pub fn new(value: u64) -> Self {
if IS_LE {
Self(value.to_le())
} else {
Self(value.to_be())
}
}
pub fn get(&self) -> u64 {
if IS_LE {
u64::from_le(self.0)
} else {
u64::from_be(self.0)
}
}
pub fn set(&mut self, value: u64) {
*self = Self::new(value);
}
}
pub type U16Le = PackedU16<true>;
pub type U16Be = PackedU16<false>;
pub type U32Le = PackedU32<true>;
pub type U32Be = PackedU32<false>;
pub type U64Le = PackedU64<true>;
pub type U64Be = PackedU64<false>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_byte_swap_16() {
assert_eq!(byte_swap_16(0x1234), 0x3412);
assert_eq!(byte_swap_16(0x00FF), 0xFF00);
}
#[test]
fn test_byte_swap_32() {
assert_eq!(byte_swap_32(0x12345678), 0x78563412);
}
#[test]
fn test_byte_swap_64() {
assert_eq!(byte_swap_64(0x0123456789ABCDEF), 0xEFCDAB8967452301);
}
#[test]
fn test_hton_ntoh_roundtrip_16() {
let original: u16 = 0xABCD;
assert_eq!(ntoh_16(hton_16(original)), original);
}
#[test]
fn test_hton_ntoh_roundtrip_32() {
let original: u32 = 0xDEADBEEF;
assert_eq!(ntoh_32(hton_32(original)), original);
}
#[test]
fn test_hton_ntoh_roundtrip_64() {
let original: u64 = 0x0123456789ABCDEF;
assert_eq!(ntoh_64(hton_64(original)), original);
}
#[test]
fn test_hton_be_equivalent_16() {
assert_eq!(hton_16(0x1234), 0x1234u16.to_be());
}
#[test]
fn test_hton_be_equivalent_32() {
assert_eq!(hton_32(0x12345678), 0x12345678u32.to_be());
}
#[test]
fn test_htole_letoh_roundtrip_32() {
let original: u32 = 0xCAFEBABE;
assert_eq!(letoh_32(htole_32(original)), original);
}
#[test]
fn test_htole_letoh_roundtrip_64() {
let original: u64 = 0xFEDCBA9876543210;
assert_eq!(letoh_64(htole_64(original)), original);
}
#[test]
fn test_read_u16_be() {
let bytes = [0x12, 0x34, 0x56];
assert_eq!(read_u16_be(&bytes, 0), Some(0x1234));
assert_eq!(read_u16_be(&bytes, 1), Some(0x3456));
assert_eq!(read_u16_be(&bytes, 2), None);
}
#[test]
fn test_read_u32_le() {
let bytes = [0x78, 0x56, 0x34, 0x12, 0xFF];
assert_eq!(read_u32_le(&bytes, 0), Some(0x12345678));
assert_eq!(read_u32_le(&bytes, 1), Some(0xFF123456));
assert_eq!(read_u32_le(&bytes, 2), None);
}
#[test]
fn test_read_u64_be() {
let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00];
assert_eq!(read_u64_be(&bytes, 0), Some(66));
assert_eq!(read_u64_be(&bytes, 1), Some(16896));
assert_eq!(read_u64_be(&bytes, 2), None);
}
#[test]
fn test_read_u16_le() {
let bytes = [0x34, 0x12];
assert_eq!(read_u16_le(&bytes, 0), Some(0x1234));
}
#[test]
fn test_read_out_of_bounds() {
let bytes = [0x01];
assert_eq!(read_u16_le(&bytes, 0), None);
assert_eq!(read_u32_le(&bytes, 0), None);
assert_eq!(read_u64_be(&bytes, 0), None);
}
#[test]
fn test_write_u16_be() {
let mut bytes = [0u8; 4];
assert!(write_u16_be(&mut bytes, 0, 0x1234));
assert_eq!(bytes[0..2], [0x12, 0x34]);
}
#[test]
fn test_write_u32_le() {
let mut bytes = [0u8; 6];
assert!(write_u32_le(&mut bytes, 1, 0xDEADBEEF));
assert_eq!(bytes[1..5], [0xEF, 0xBE, 0xAD, 0xDE]);
}
#[test]
fn test_write_u64_be() {
let mut bytes = [0u8; 8];
assert!(write_u64_be(&mut bytes, 0, 0x0102030405060708));
assert_eq!(bytes, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
}
#[test]
fn test_write_oob() {
let mut bytes = [0u8; 3];
assert!(!write_u32_le(&mut bytes, 0, 0x12345678));
assert!(!write_u64_be(&mut bytes, 0, 0));
assert!(write_u16_le(&mut bytes, 1, 0xABCD)); }
#[test]
fn test_write_read_roundtrip_be() {
let mut bytes = [0u8; 100];
write_u32_be(&mut bytes, 10, 0xCAFEBABE);
assert_eq!(read_u32_be(&bytes, 10), Some(0xCAFEBABE));
}
#[test]
fn test_write_read_roundtrip_le() {
let mut bytes = [0u8; 100];
write_u64_le(&mut bytes, 5, 0xFEDCBA9876543210);
assert_eq!(read_u64_le(&bytes, 5), Some(0xFEDCBA9876543210));
}
#[test]
fn test_is_little_endian() {
assert!(is_little_endian()); assert!(!is_big_endian());
}
#[test]
fn test_endianness_native() {
assert_eq!(Endianness::native(), Endianness::Little);
}
#[test]
fn test_endianness_network() {
assert_eq!(Endianness::network(), Endianness::Big);
}
#[test]
fn test_endianness_read_write_le() {
let mut bytes = [0u8; 10];
let le = Endianness::Little;
le.write_u32(&mut bytes, 2, 0x12345678);
assert_eq!(le.read_u32(&bytes, 2), Some(0x12345678));
assert_ne!(Endianness::Big.read_u32(&bytes, 2), Some(0x12345678));
}
#[test]
fn test_packed_u16_le() {
let packed = U16Le::new(0x1234);
assert_eq!(packed.get(), 0x1234);
}
#[test]
fn test_packed_u16_be() {
let packed = U16Be::new(0x1234);
assert_eq!(packed.get(), 0x1234);
}
#[test]
fn test_packed_u32_le() {
let packed = U32Le::new(0xDEADBEEF);
assert_eq!(packed.get(), 0xDEADBEEF);
}
#[test]
fn test_packed_u32_be() {
let packed = U32Be::new(0xCAFEBABE);
assert_eq!(packed.get(), 0xCAFEBABE);
}
#[test]
fn test_packed_u64_le() {
let packed = U64Le::new(0x0123456789ABCDEF);
assert_eq!(packed.get(), 0x0123456789ABCDEF);
}
#[test]
fn test_packed_u64_be() {
let packed = U64Be::new(0xFEDCBA9876543210);
assert_eq!(packed.get(), 0xFEDCBA9876543210);
}
#[test]
fn test_packed_set() {
let mut packed = U32Le::new(0x100);
packed.set(0x200);
assert_eq!(packed.get(), 0x200);
}
#[test]
fn test_packed_le_be_distinct() {
let le = U32Le::new(0x01020304);
let be = U32Be::new(0x01020304);
assert_ne!(le.0, be.0);
assert_eq!(le.get(), be.get());
}
#[test]
fn test_identity() {
assert_eq!(htole_32(0x12345678), 0x12345678u32.to_le());
assert_eq!(byte_swap_16(byte_swap_16(0xABCD)), 0xABCD);
assert_eq!(byte_swap_32(byte_swap_32(0xDEADBEEF)), 0xDEADBEEF);
}
#[test]
fn test_read_empty_slice() {
let empty: [u8; 0] = [];
assert_eq!(read_u16_le(&empty, 0), None);
assert_eq!(read_u32_be(&empty, 0), None);
}
}