#![allow(
unsafe_code,
reason = "SBE encoder needs uninit buffer writes to avoid a memset pass"
)]
use std::mem::MaybeUninit;
#[derive(Debug)]
pub struct SbeWriter<'a> {
buf: &'a mut [MaybeUninit<u8>],
pos: usize,
}
impl<'a> SbeWriter<'a> {
#[inline]
pub fn new(buf: &'a mut [u8]) -> Self {
let uninit = unsafe {
std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast::<MaybeUninit<u8>>(), buf.len())
};
Self {
buf: uninit,
pos: 0,
}
}
#[inline]
pub fn new_uninit(buf: &'a mut [MaybeUninit<u8>]) -> Self {
Self { buf, pos: 0 }
}
#[inline]
#[must_use]
pub const fn pos(&self) -> usize {
self.pos
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.buf.len()
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.buf.is_empty()
}
#[inline]
pub fn write_u8(&mut self, value: u8) {
self.buf[self.pos].write(value);
self.pos += 1;
}
#[inline]
pub fn write_i8(&mut self, value: i8) {
self.buf[self.pos].write(value as u8);
self.pos += 1;
}
#[inline]
pub fn write_u16_le(&mut self, value: u16) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_i16_le(&mut self, value: i16) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_u32_le(&mut self, value: u32) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_i32_le(&mut self, value: i32) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_u64_le(&mut self, value: u64) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_i64_le(&mut self, value: i64) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_u128_le(&mut self, value: u128) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_i128_le(&mut self, value: i128) {
self.write_array(value.to_le_bytes());
}
#[inline]
pub fn write_bytes(&mut self, bytes: &[u8]) {
let end = self.pos + bytes.len();
let dst_ptr = self.buf[self.pos..end].as_mut_ptr().cast::<u8>();
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), dst_ptr, bytes.len());
}
self.pos = end;
}
#[inline]
fn write_array<const N: usize>(&mut self, bytes: [u8; N]) {
let end = self.pos + N;
let dst_ptr = self.buf[self.pos..end].as_mut_ptr().cast::<[u8; N]>();
unsafe {
*dst_ptr = bytes;
}
self.pos = end;
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_new_starts_at_zero() {
let mut buf = [0u8; 4];
let writer = SbeWriter::new(&mut buf);
assert_eq!(writer.pos(), 0);
assert_eq!(writer.len(), 4);
}
#[rstest]
fn test_write_u8() {
let mut buf = [0u8; 2];
let mut writer = SbeWriter::new(&mut buf);
writer.write_u8(0x42);
writer.write_u8(0xFF);
assert_eq!(writer.pos(), 2);
assert_eq!(buf, [0x42, 0xFF]);
}
#[rstest]
fn test_write_u16_le() {
let mut buf = [0u8; 2];
let mut writer = SbeWriter::new(&mut buf);
writer.write_u16_le(0x1234);
assert_eq!(writer.pos(), 2);
assert_eq!(buf, [0x34, 0x12]);
}
#[rstest]
fn test_write_i64_le() {
let value: i64 = -1_234_567_890_123_456_789;
let mut buf = [0u8; 8];
let mut writer = SbeWriter::new(&mut buf);
writer.write_i64_le(value);
assert_eq!(writer.pos(), 8);
assert_eq!(buf, value.to_le_bytes());
}
#[rstest]
fn test_write_u128_le() {
let value: u128 = 0x0123_4567_89AB_CDEF_FEDC_BA98_7654_3210;
let mut buf = [0u8; 16];
let mut writer = SbeWriter::new(&mut buf);
writer.write_u128_le(value);
assert_eq!(writer.pos(), 16);
assert_eq!(buf, value.to_le_bytes());
}
#[rstest]
fn test_write_bytes() {
let mut buf = [0u8; 5];
let mut writer = SbeWriter::new(&mut buf);
writer.write_bytes(&[1, 2, 3]);
writer.write_bytes(&[4, 5]);
assert_eq!(writer.pos(), 5);
assert_eq!(buf, [1, 2, 3, 4, 5]);
}
#[rstest]
fn test_write_into_uninit() {
let mut buf: [MaybeUninit<u8>; 4] = [const { MaybeUninit::uninit() }; 4];
let mut writer = SbeWriter::new_uninit(&mut buf);
writer.write_u8(0xAA);
writer.write_u16_le(0x1234);
writer.write_u8(0xBB);
assert_eq!(writer.pos(), 4);
let initialized: [u8; 4] = unsafe { std::mem::transmute(buf) };
assert_eq!(initialized, [0xAA, 0x34, 0x12, 0xBB]);
}
#[rstest]
#[should_panic(expected = "index out of bounds")]
fn test_write_past_end_panics() {
let mut buf = [0u8; 2];
let mut writer = SbeWriter::new(&mut buf);
writer.write_u8(0);
writer.write_u8(0);
writer.write_u8(0);
}
}