use core::fmt;
#[cfg(feature = "std")]
use std::error;
use crate::phys::EndianOrder;
type U16Encoder = fn(value: u16) -> [u8; 2];
type U32Encoder = fn(value: u32) -> [u8; 4];
type U64Encoder = fn(value: u64) -> [u8; 8];
struct EndianEncoderImpl {
order: EndianOrder,
put_u16: U16Encoder,
put_u32: U32Encoder,
put_u64: U64Encoder,
}
const BIG_ENDIAN_ENCODER: EndianEncoderImpl = EndianEncoderImpl {
order: EndianOrder::Big,
put_u16: u16::to_be_bytes,
put_u32: u32::to_be_bytes,
put_u64: u64::to_be_bytes,
};
const LITTLE_ENDIAN_ENCODER: EndianEncoderImpl = EndianEncoderImpl {
order: EndianOrder::Little,
put_u16: u16::to_le_bytes,
put_u32: u32::to_le_bytes,
put_u64: u64::to_le_bytes,
};
pub struct EndianEncoder<'a> {
data: &'a mut [u8],
offset: usize,
encoder: EndianEncoderImpl,
}
impl fmt::Debug for EndianEncoder<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EndianEncoder")
.field("length", &self.data.len())
.field("offset", &self.offset)
.field("order", &self.encoder.order)
.finish()
}
}
impl EndianEncoder<'_> {
pub fn to_bytes(data: &mut [u8], order: EndianOrder) -> EndianEncoder<'_> {
EndianEncoder {
data,
offset: 0,
encoder: match order {
EndianOrder::Big => BIG_ENDIAN_ENCODER,
EndianOrder::Little => LITTLE_ENDIAN_ENCODER,
},
}
}
fn check_need(&self, count: usize) -> Result<(), EndianEncodeError> {
if self.available() >= count {
Ok(())
} else {
Err(EndianEncodeError::EndOfOutput {
offset: self.offset,
capacity: self.capacity(),
count,
})
}
}
pub fn available(&self) -> usize {
self.data.len().saturating_sub(self.offset)
}
pub fn capacity(&self) -> usize {
self.data.len()
}
pub fn offset(&self) -> usize {
self.offset
}
pub fn order(&self) -> EndianOrder {
self.encoder.order
}
pub fn is_empty(&self) -> bool {
self.offset == 0
}
pub fn is_full(&self) -> bool {
self.offset >= self.data.len()
}
pub fn len(&self) -> usize {
self.offset
}
fn put_2_bytes(&mut self, data: [u8; 2]) -> Result<(), EndianEncodeError> {
self.check_need(2)?;
let start = self.offset;
let end = start + 2;
self.offset = end;
self.data[start..end].copy_from_slice(&data);
Ok(())
}
fn put_4_bytes(&mut self, data: [u8; 4]) -> Result<(), EndianEncodeError> {
self.check_need(4)?;
let start = self.offset;
let end = start + 4;
self.offset = end;
self.data[start..end].copy_from_slice(&data);
Ok(())
}
fn put_8_bytes(&mut self, data: [u8; 8]) -> Result<(), EndianEncodeError> {
self.check_need(8)?;
let start = self.offset;
let end = start + 8;
self.offset = end;
self.data[start..end].copy_from_slice(&data);
Ok(())
}
pub fn put_bytes(&mut self, data: &[u8]) -> Result<(), EndianEncodeError> {
let length = data.len();
self.check_need(length)?;
let start = self.offset;
let end = start + length;
self.offset = end;
self.data[start..end].copy_from_slice(data);
Ok(())
}
pub fn put_u8(&mut self, value: u8) -> Result<(), EndianEncodeError> {
self.check_need(1)?;
self.data[self.offset] = value;
self.offset += 1;
Ok(())
}
pub fn put_u16(&mut self, value: u16) -> Result<(), EndianEncodeError> {
self.put_2_bytes((self.encoder.put_u16)(value))
}
pub fn put_u32(&mut self, value: u32) -> Result<(), EndianEncodeError> {
self.put_4_bytes((self.encoder.put_u32)(value))
}
pub fn put_u64(&mut self, value: u64) -> Result<(), EndianEncodeError> {
self.put_8_bytes((self.encoder.put_u64)(value))
}
pub fn put_zero_padding(&mut self, length: usize) -> Result<(), EndianEncodeError> {
self.check_need(length)?;
let start = self.offset;
let end = start + length;
self.offset = end;
self.data[start..end].fill(0);
Ok(())
}
}
#[derive(Debug)]
pub enum EndianEncodeError {
EndOfOutput {
offset: usize,
capacity: usize,
count: usize,
},
}
impl fmt::Display for EndianEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EndianEncodeError::EndOfOutput {
offset,
capacity,
count,
} => {
write!(
f,
"Endian end of output at offset {offset} capacity {capacity} count {count}"
)
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for EndianEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}