pub trait BlackboxWriter {
fn write_byte(&mut self, byte: u8);
fn write_char(&mut self, c: char) {
self.write_byte(c as u8);
}
fn write_str(&mut self, s: &str) {
for b in s.as_bytes() {
self.write_byte(*b);
}
}
fn write_h_str(&mut self, s: &str) {
self.write_str("H ");
self.write_str(s);
}
fn write_u8_ascii(&mut self, mut n: u8) {
if n == 0 {
self.write_char('0');
return;
}
let mut buf = [0u8; 3];
let mut i = 0;
while n > 0 {
buf[i] = (n % 10) + b'0';
n /= 10;
i += 1;
}
for j in (0..i).rev() {
self.write_char(buf[j] as char);
}
}
#[allow(clippy::cast_possible_truncation)]
fn write_u32_ascii(&mut self, mut n: u32) {
if n == 0 {
self.write_char('0');
return;
}
let mut buf = [0u8; 16];
let mut i = 0;
while n > 0 {
buf[i] = ((n % 10) + u32::from(b'0')) as u8;
n /= 10;
i += 1;
}
for j in (0..i).rev() {
self.write_char(buf[j] as char);
}
}
fn write_i32_ascii(&mut self, mut n: i32) {
if n < 0 {
self.write_char('-');
n = -n;
}
self.write_u32_ascii(n.cast_unsigned());
}
fn write_h_str_u32_ascii(&mut self, s: &str, n: u32) {
self.write_h_str(s);
self.write_u32_ascii(n);
self.write_char('\n');
}
fn write_h_str_i32_ascii(&mut self, s: &str, n: i32) {
self.write_h_str(s);
self.write_i32_ascii(n);
self.write_char('\n');
}
fn write_h_str_u16_ascii(&mut self, s: &str, n: u16) {
self.write_h_str_u32_ascii(s, u32::from(n));
}
fn write_h_str_i16_ascii(&mut self, s: &str, n: i16) {
self.write_h_str_i32_ascii(s, i32::from(n));
}
}
pub fn write_field_line<'a, T, I, F>(
writer: &mut dyn BlackboxWriter,
frame_type: char,
label: &str,
fields: I,
mut op: F,
) where
T: 'a,
I: Iterator<Item = &'a T>,
F: FnMut(&mut dyn BlackboxWriter, &T),
{
writer.write_str("H Field ");
writer.write_char(frame_type);
writer.write_char(' ');
writer.write_str(label);
writer.write_char(':');
for (i, field) in fields.enumerate() {
if i > 0 {
writer.write_char(',');
}
op(writer, field);
}
writer.write_char('\n');
}
#[derive(Default)]
pub struct SliceWriter<'a> {
pub buffer: &'a mut [u8],
pub pos: usize,
}
impl SliceWriter<'_> {
pub fn new() -> Self {
Self::default()
}
}
impl BlackboxWriter for SliceWriter<'_> {
fn write_byte(&mut self, byte: u8) {
if self.pos < self.buffer.len() {
self.buffer[self.pos] = byte;
self.pos += 1;
}
}
}
impl SliceWriter<'_> {
pub fn begin_frame(&mut self, value: u8) {
self.write_byte(value);
}
pub fn end_frame(&self) -> usize {
self.pos
}
pub fn write_unsigned_vb(&mut self, mut value: u32) {
while value > 127 {
#[allow(clippy::cast_possible_truncation)]
self.write_byte(((value & 0x7F) | 0x80) as u8);
value >>= 7;
}
#[allow(clippy::cast_possible_truncation)]
self.write_byte(value as u8);
}
pub fn write_f32(&mut self, value: f32) {
let bits = value.to_bits().cast_signed();
self.write_unsigned_vb(bits.cast_unsigned());
}
pub fn write_unsigned_vb_16(&mut self, value: u16) {
self.write_unsigned_vb(u32::from(value));
}
pub fn write_unsigned_vb_array(&mut self, values: &[u32]) {
for &value in values {
self.write_unsigned_vb(value);
}
}
pub fn write_unsigned_vb_16_array(&mut self, values: &[u16]) {
for &value in values {
self.write_unsigned_vb_16(value);
}
}
#[inline]
pub const fn zigzag_encode(value: i32) -> u32 {
((value << 1) ^ (value >> 31)).cast_unsigned()
}
pub fn write_signed_vb(&mut self, value: i32) {
self.write_unsigned_vb(Self::zigzag_encode(value));
}
pub fn write_signed_vb_16(&mut self, value: i16) {
self.write_signed_vb(i32::from(value));
}
pub fn write_signed_vb_array(&mut self, values: &[i32]) {
for &value in values {
self.write_signed_vb(value);
}
}
pub fn write_signed_vb_16_array(&mut self, values: &[i16]) {
for &value in values {
self.write_signed_vb_16(value);
}
}
pub fn write_tag8_8svb(&mut self, values: &[i32; 8]) {
if values.is_empty() {
return;
}
if values.len() == 1 {
self.write_signed_vb(values[0]);
return;
}
let mut header: u8 = 0;
for (ii, value) in values.iter().enumerate() {
if *value != 0 {
header |= 1 << ii;
}
}
self.write_byte(header);
for value in values {
if *value != 0 {
self.write_signed_vb(*value);
}
}
}
pub fn write_tag8_4s16(&mut self, values: [i16; 4]) {
const BITS_0: u8 = 0;
const BITS_4: u8 = 1;
const BITS_8: u8 = 2;
const BITS_16: u8 = 3;
let mut tag: u8 = 0;
for (ii, val) in values.iter().enumerate() {
let size_code = if *val == 0 {
BITS_0 } else if (-8..8).contains(val) {
BITS_4 } else if (-128..128).contains(val) {
BITS_8 } else {
BITS_16 };
tag |= size_code << (ii * 2);
}
self.write_byte(tag);
let mut buffer: Option<u8> = None;
for value in values {
match tag & 0x03 {
BITS_4 => {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
if let Some(nibble) = buffer {
self.write_byte(nibble | (value & 0x0F) as u8);
buffer = None;
} else {
buffer = Some((value << 4) as u8);
}
}
BITS_8 => {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
if let Some(nibble) = buffer {
self.write_byte(nibble | ((value >> 4) & 0x0F) as u8);
buffer = Some((value << 4) as u8);
} else {
self.write_byte(value as u8);
}
}
BITS_16 => {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
if let Some(nibble) = buffer {
self.write_byte(nibble | ((value >> 12) & 0x0F) as u8);
self.write_byte((value >> 4) as u8);
buffer = Some((value << 4) as u8);
} else {
let bytes = value.to_le_bytes();
self.write_byte(bytes[0]);
self.write_byte(bytes[1]);
}
}
_ => {} }
tag >>= 2;
}
if let Some(lone_nibble) = buffer {
self.write_byte(lone_nibble);
}
}
pub fn write_tag2_3s32(&mut self, values: [i32; 3]) {
const BITS_2: u8 = 0;
const BITS_4: u8 = 1;
const BITS_6: u8 = 2;
const BITS_32: u8 = 3;
let mut bits_needed = BITS_2;
for &val in &values {
let needed = if val == 0 {
BITS_2
} else if (-8..8).contains(&val) {
BITS_4
} else if (-128..128).contains(&val) {
BITS_6
} else {
BITS_32
};
if needed > bits_needed {
bits_needed = needed;
}
}
self.write_byte(bits_needed);
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
match bits_needed {
BITS_4 => {
self.write_byte(((values[0] as u8) & 0x0F) | (((values[1] as u8) & 0x0F) << 4));
self.write_byte((values[2] as u8) & 0x0F);
}
BITS_6 => {
for &v in &values {
self.write_byte(v as u8);
}
}
BITS_32 => {
for &v in &values {
let b = (v as i16).to_le_bytes();
self.write_byte(b[0]);
self.write_byte(b[1]);
}
}
_ => {} }
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_writer(buf: &mut [u8]) -> SliceWriter<'_> {
SliceWriter { buffer: buf, pos: 0 }
}
#[test]
fn write_byte() {
let mut buf = [0u8; 2];
{
let mut writer = create_writer(&mut buf);
writer.write_byte(10);
writer.write_byte(20);
writer.write_byte(30); assert_eq!(writer.pos, 2);
}
assert_eq!(buf, [10, 20]);
}
#[test]
fn unsigned_vb() {
let mut buf = [0u8; 5];
{
let mut writer = create_writer(&mut buf);
writer.write_unsigned_vb(127);
writer.write_unsigned_vb(128);
}
assert_eq!(buf[0], 127);
assert_eq!(buf[1..3], [0x80, 0x01]);
}
#[test]
fn signed_vb_zigzag() {
let mut buf = [0u8; 5];
let mut writer = create_writer(&mut buf);
writer.write_signed_vb(0);
writer.write_signed_vb(-1);
writer.write_signed_vb(1);
assert_eq!(buf[..3], [0, 1, 2]);
}
#[test]
fn write_tag8_8svb() {
let mut buf = [0u8; 10];
let mut writer = create_writer(&mut buf);
let values = [1, 0, -1, 0, 0, 0, 0, 0];
writer.write_tag8_8svb(&values);
assert_eq!(buf[..3], [5, 2, 1]);
}
#[test]
fn write_tag8_4s16_mixed() {
let mut buf = [0u8; 10];
let mut writer = create_writer(&mut buf);
let values: [i16; 4] = [0, 3, 200, 500];
writer.write_tag8_4s16(values);
assert_eq!(buf[0], 0b_11_11_01_00);
assert_eq!(buf[1], 0b_0011_0000); assert_eq!(buf[2], 0b_0000_1100);
assert_eq!(buf[3], 0b_1000_0000);
assert_eq!(buf[4], 0b_0001_1111);
}
#[test]
fn write_tag2_3s32_nibbles() {
let mut buf = [0u8; 10];
let mut writer = create_writer(&mut buf);
let values = [2, -1, 5];
writer.write_tag2_3s32(values);
assert_eq!(buf[..3], [1, 0xF2, 0x05]);
}
#[test]
fn write_signed_vb_16_array() {
let mut buf = [0u8; 10];
let mut writer = create_writer(&mut buf);
let values = [0i16, -1i16];
writer.write_signed_vb_16_array(&values);
assert_eq!(buf[..2], [0, 1]);
}
}
#[cfg(test)]
mod edge_case_tests {
use super::*;
#[test]
fn boundary_16bit_signed_vb() {
let mut buf = [0u8; 10];
{
let mut writer = SliceWriter { buffer: &mut buf, pos: 0 };
writer.write_signed_vb(32767);
writer.write_signed_vb(-32768);
}
assert_eq!(buf[..3], [0xFE, 0xFF, 0x03]);
assert_eq!(buf[3..6], [0xFF, 0xFF, 0x03]);
}
#[test]
fn tag8_4s16_boundary_values() {
let mut buf = [0u8; 10];
{
let mut writer = SliceWriter { buffer: &mut buf, pos: 0 };
let values = [-8, -128, 127, 32767];
writer.write_tag8_4s16(values);
}
assert_eq!(buf[0], 0b_11_10_10_01);
assert_eq!(buf[1], 136);
assert_eq!(buf[2], 7);
assert_eq!(buf[3], 247);
}
#[test]
fn buffer_overflow_safety() {
let mut small_buf = [0u8; 1];
{
let mut writer = SliceWriter { buffer: &mut small_buf, pos: 0 };
writer.write_unsigned_vb(30000);
assert_eq!(writer.pos, 1);
}
assert_eq!(small_buf[0], 0xB0); }
#[test]
fn tag2_3s32_max_i16_range() {
let mut buf = [0u8; 10];
{
let mut writer = SliceWriter { buffer: &mut buf, pos: 0 };
let values = [0, 128, 0];
writer.write_tag2_3s32(values);
}
assert_eq!(buf[0], 3); assert_eq!(&buf[1..7], &[0, 0, 128, 0, 0, 0]);
}
}