Skip to main content

rustmeter_beacon_core/
buffer.rs

1use core::mem::MaybeUninit;
2
3use crate::{tracing::ReadTracingError, varint::VarIntWritable};
4
5/// Internal buffer writer for tracing events using a fixed-size buffer with uninitialized memory for efficiency
6pub struct BufferWriter {
7    buffer: [MaybeUninit<u8>; 32],
8    position: usize,
9}
10
11impl BufferWriter {
12    pub fn new() -> Self {
13        BufferWriter {
14            buffer: [MaybeUninit::uninit(); 32],
15            position: 0,
16        }
17    }
18
19    pub fn write_byte(&mut self, byte: u8) {
20        self.buffer[self.position] = MaybeUninit::new(byte);
21        self.position += 1;
22    }
23
24    /// Writes a slice of bytes into the buffer. Assumes there is enough space
25    pub fn write_bytes(&mut self, data: &[u8]) {
26        let len = data.len();
27        self.buffer[self.position..self.position + len]
28            .copy_from_slice(unsafe { core::mem::transmute::<&[u8], &[MaybeUninit<u8>]>(data) });
29        self.position += len;
30    }
31
32    /// Writes a generic integer using LEB128 (VarInt) encoding
33    #[inline]
34    pub fn write_varint<T: VarIntWritable>(&mut self, mut value: T) {
35        loop {
36            let mut byte = value.low_7_bits();
37
38            value.shr_7();
39            if !value.is_zero() {
40                // More bytes to come, set continuation bit
41                byte |= 0x80;
42                self.write_byte(byte);
43            } else {
44                // Last byte, no continuation bit
45                self.write_byte(byte);
46                break;
47            }
48        }
49    }
50
51    /// Returns the already written data as a slice
52    pub fn as_slice(&self) -> &[u8] {
53        &unsafe { core::mem::transmute::<&[MaybeUninit<u8>], &[u8]>(&self.buffer[..self.position]) }
54    }
55
56    pub fn len(&self) -> usize {
57        self.position
58    }
59}
60
61/// Simple buffer reader for reading bytes from a slice
62pub struct BufferReader<'a> {
63    buffer: &'a [u8],
64    position: usize,
65}
66
67impl<'a> BufferReader<'a> {
68    pub fn new(buffer: &'a [u8]) -> Self {
69        BufferReader {
70            buffer,
71            position: 0,
72        }
73    }
74
75    pub fn len(&self) -> usize {
76        self.buffer.len()
77    }
78
79    /// Reads a single byte from the buffer. Returns None if end of buffer is reached.
80    pub fn read_byte(&mut self) -> Result<u8, ReadTracingError> {
81        if self.position >= self.buffer.len() {
82            return Err(ReadTracingError::InsufficientData);
83        }
84
85        let byte = self.buffer[self.position];
86        self.position += 1;
87        Ok(byte)
88    }
89
90    /// Reads a slice of bytes of the given length from the buffer. Returns None if not enough data is available.
91    pub fn read_bytes(&mut self, length: usize) -> Result<&[u8], ReadTracingError> {
92        if self.position + length > self.buffer.len() {
93            return Err(ReadTracingError::InsufficientData);
94        }
95
96        let bytes = &self.buffer[self.position..self.position + length];
97        self.position += length;
98        Ok(bytes)
99    }
100
101    /// Reads a LEB128 (VarInt) encoded u64.
102    pub fn read_varint(&mut self) -> Result<u64, ReadTracingError> {
103        let mut result = 0u64;
104        let mut shift = 0;
105
106        loop {
107            let byte = self.read_byte()?;
108
109            // Mask out the continuation bit and shift into result
110            result |= ((byte & 0x7F) as u64) << shift;
111
112            // If continuation bit is not set, we are done
113            if (byte & 0x80) == 0 {
114                return Ok(result);
115            }
116
117            shift += 7;
118
119            // Protection against overflow / bad data (max 10 bytes for u64)
120            if shift >= 70 {
121                return Err(ReadTracingError::VarIntOverflow);
122            }
123        }
124    }
125
126    /// Reads a little-endian u16 from the buffer
127    pub fn read_u16(&mut self) -> Result<u16, ReadTracingError> {
128        let bytes = self.read_bytes(2)?;
129        Ok(u16::from_le_bytes(bytes.try_into().unwrap()))
130    }
131
132    /// Reads a little-endian u32 from the buffer
133    pub fn read_u32(&mut self) -> Result<u32, ReadTracingError> {
134        let bytes = self.read_bytes(4)?;
135        Ok(u32::from_le_bytes(bytes.try_into().unwrap()))
136    }
137
138    /// Reads a little-endian u64 from the buffer
139    pub fn read_u64(&mut self) -> Result<u64, ReadTracingError> {
140        let bytes = self.read_bytes(8)?;
141        Ok(u64::from_le_bytes(bytes.try_into().unwrap()))
142    }
143
144    pub fn get_position(&self) -> usize {
145        self.position
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn test_buffer_writer() {
155        let mut writer = BufferWriter::new();
156        writer.write_byte(0x12);
157        writer.write_bytes(&[0x34, 0x56, 0x78]);
158
159        let written = writer.as_slice();
160        assert_eq!(written, &[0x12, 0x34, 0x56, 0x78]);
161    }
162
163    #[test]
164    fn test_buffer_reader() {
165        let data = [0x9A, 0xBC, 0xDE, 0xF0];
166        let mut reader = BufferReader::new(&data);
167
168        assert_eq!(reader.read_byte(), Ok(0x9A));
169        assert_eq!(reader.read_bytes(2), Ok(&[0xBC, 0xDE][..]));
170        assert_eq!(reader.read_byte(), Ok(0xF0));
171        assert_eq!(reader.read_byte(), Err(ReadTracingError::InsufficientData));
172    }
173
174    #[test]
175    fn test_buffer_write_and_read() {
176        // Write data
177        let mut writer = BufferWriter::new();
178        writer.write_bytes(&[0x01, 0x02, 0x03, 0x04, 0x05]);
179        writer.write_varint(1u8);
180        writer.write_varint(300u16);
181        writer.write_varint(70000u32);
182        writer.write_varint(123456789u64);
183        writer.write_bytes(&[0x01, 0x02, 0x03, 0x04, 0x05]);
184
185        // Read data
186        let data = writer.as_slice();
187        let mut reader = BufferReader::new(data);
188        assert_eq!(
189            reader.read_bytes(5),
190            Ok(&[0x01, 0x02, 0x03, 0x04, 0x05][..])
191        );
192        assert_eq!(reader.read_varint(), Ok(1u64));
193        assert_eq!(reader.read_varint(), Ok(300u64));
194        assert_eq!(reader.read_varint(), Ok(70000u64));
195        assert_eq!(reader.read_varint(), Ok(123456789u64));
196        assert_eq!(
197            reader.read_bytes(5),
198            Ok(&[0x01, 0x02, 0x03, 0x04, 0x05][..])
199        );
200    }
201}