use core::num::NonZeroUsize;
use core::ops;
use super::error::Error;
use super::truncate::TruncateNum;
use super::width::Widths;
pub struct Encoder<'d, U> {
data: &'d mut [u8],
bit_pos: usize,
bytes_committed: usize,
field_widths: Widths,
unit: U,
hart_index_width: u8,
timestamp_width: u8,
trace_type_width: u8,
compress: bool,
}
impl<'d, U> Encoder<'d, U> {
pub(super) fn new(
field_widths: Widths,
unit: U,
hart_index_width: u8,
timestamp_width: u8,
trace_type_width: u8,
compress: bool,
) -> Self {
Self {
data: &mut [],
bit_pos: 0,
bytes_committed: 0,
field_widths,
unit,
hart_index_width,
timestamp_width,
trace_type_width,
compress,
}
}
pub fn reset(&mut self, data: &'d mut [u8]) {
self.data = data;
self.bit_pos = 0;
self.bytes_committed = 0;
}
pub fn uncommitted(&self) -> usize {
self.data.len() - self.bytes_committed
}
pub fn encode(&mut self, data: &impl Encode<'d, U>) -> Result<(), Error> {
data.encode(self)
}
pub fn unit(&self) -> &U {
&self.unit
}
pub(super) fn widths(&self) -> &Widths {
&self.field_widths
}
pub(super) fn hart_index_width(&self) -> u8 {
self.hart_index_width
}
pub(super) fn timestamp_width(&self) -> u8 {
self.timestamp_width
}
pub(super) fn trace_type_width(&self) -> u8 {
self.trace_type_width
}
pub(super) fn first_uncommitted_chunk<const N: usize>(
&mut self,
) -> Result<&'d mut [u8; N], Error> {
let (chunk, data) = core::mem::take(&mut self.data)
.split_at_mut_checked(self.bytes_committed)
.and_then(|(_, d)| d.split_first_chunk_mut())
.ok_or_else(|| {
self.reset(&mut []);
Error::BufferTooSmall
})?;
self.reset(data);
Ok(chunk)
}
pub(super) fn write_bit(&mut self, bit: bool) -> Result<(), Error> {
let byte_pos = self.bit_pos >> 3;
let byte = self.get_byte(byte_pos)?;
let mask = 0xff << (self.bit_pos & 0x7);
let byte = if bit { byte | mask } else { byte & !mask };
self.write_byte(byte, byte_pos)?;
self.bit_pos += 1;
Ok(())
}
pub(super) fn write_differential_bit(&mut self, bit: bool) -> Result<(), Error> {
let byte_pos = self.bit_pos >> 3;
let mut byte = self.get_byte(byte_pos)?;
if bit {
byte ^= 0xff << (self.bit_pos & 0x7);
}
self.write_byte(byte, byte_pos)?;
self.bit_pos += 1;
Ok(())
}
pub(super) fn write_bits<T>(&mut self, bits: T, bit_count: u8) -> Result<(), Error>
where
T: Copy
+ ops::Shl<usize, Output = T>
+ ops::Shr<usize, Output = T>
+ ops::BitOrAssign<T>
+ TruncateNum,
{
let Some(bit_count) = NonZeroUsize::new(bit_count.into()) else {
return Ok(());
};
let bit_pos = self.bit_pos & 0x07;
let mut byte_pos = self.bit_pos >> 3;
let mut byte = self.get_byte(byte_pos)?;
byte &= (1 << bit_pos) - 1;
byte |= bits.lsb() << bit_pos;
let mut bits_written = 8 - bit_pos;
while bits_written < bit_count.get() {
self.write_byte(byte, byte_pos)?;
byte_pos += 1;
byte = (bits >> bits_written).lsb();
bits_written += 8;
}
if let Some(upper) = NonZeroUsize::new(bits_written - bit_count.get()) {
let mask = !(0xff >> upper.get());
if (bits >> (bit_count.get() - 1)).lsb() & 1 != 0 {
byte |= mask;
} else {
byte &= !mask;
}
}
self.write_byte(byte, byte_pos)?;
self.bit_pos += bit_count.get();
Ok(())
}
fn get_byte(&mut self, byte_pos: usize) -> Result<u8, Error> {
if byte_pos < self.bytes_committed {
return self
.data
.get(byte_pos)
.copied()
.ok_or(Error::BufferTooSmall);
}
let last_committed = self.bytes_committed.saturating_sub(1);
let last_committed = self.data.get(last_committed).ok_or(Error::BufferTooSmall)?;
if last_committed & 0x80 != 0 {
Ok(0xff)
} else {
Ok(0x00)
}
}
fn write_byte(&mut self, byte: u8, byte_pos: usize) -> Result<(), Error> {
let data: &mut [u8] = self.data;
let split = data
.split_at_mut_checked(byte_pos)
.map(|(d, t)| (d, t.first_mut()));
let (data, target) = if let Some(split) = split {
split
} else {
(data, None)
};
if let Some((extend, fill)) = data
.split_at_mut_checked(self.bytes_committed)
.and_then(|(c, f)| c.last().map(|e| (e & 0x80 != 0, f)))
{
if self.compress && matches!((byte, extend), (0x00, false) | (0xff, true)) {
return Ok(());
}
fill.fill(if extend { 0xff } else { 0x00 });
}
*target.ok_or(Error::BufferTooSmall)? = byte;
self.bytes_committed = byte_pos + 1;
Ok(())
}
}
pub trait Encode<'d, U>: Sized {
fn encode(&self, encoder: &mut Encoder<'d, U>) -> Result<(), Error>;
}