Skip to main content

ironsbe_core/
encoder.rs

1//! Encoder traits for SBE messages.
2//!
3//! This module provides the [`SbeEncoder`] trait for message encoding.
4
5use crate::header::MessageHeader;
6
7/// Trait for SBE message encoders.
8///
9/// Implementations wrap a mutable byte buffer and provide field setters
10/// that write directly to the buffer.
11///
12/// # Example
13/// ```ignore
14/// // Generated encoder usage
15/// let mut buffer = [0u8; 256];
16/// let mut encoder = NewOrderSingleEncoder::wrap(&mut buffer, 0);
17/// encoder
18///     .set_symbol(b"AAPL    ")
19///     .set_quantity(100)
20///     .set_price_mantissa(15050)
21///     .set_price_exponent(-2);
22/// let len = encoder.encoded_length();
23/// ```
24pub trait SbeEncoder: Sized {
25    /// Schema template ID for this message type.
26    const TEMPLATE_ID: u16;
27
28    /// Schema ID.
29    const SCHEMA_ID: u16;
30
31    /// Schema version.
32    const SCHEMA_VERSION: u16;
33
34    /// Block length (fixed portion size in bytes).
35    const BLOCK_LENGTH: u16;
36
37    /// Wraps a mutable buffer for encoding.
38    ///
39    /// This automatically writes the message header.
40    ///
41    /// # Arguments
42    /// * `buffer` - Mutable byte buffer to write to
43    /// * `offset` - Byte offset where the message starts
44    ///
45    /// # Returns
46    /// An encoder instance wrapping the buffer.
47    fn wrap(buffer: &mut [u8], offset: usize) -> Self;
48
49    /// Returns the final encoded length after all writes.
50    ///
51    /// This includes the header and all written portions.
52    fn encoded_length(&self) -> usize;
53
54    /// Creates the message header for this encoder.
55    #[must_use]
56    fn create_header() -> MessageHeader {
57        MessageHeader {
58            block_length: Self::BLOCK_LENGTH,
59            template_id: Self::TEMPLATE_ID,
60            schema_id: Self::SCHEMA_ID,
61            version: Self::SCHEMA_VERSION,
62        }
63    }
64}
65
66/// Helper struct for building encoded messages.
67///
68/// Provides a convenient way to track the current write position
69/// and manage buffer space.
70#[derive(Debug)]
71pub struct EncoderBuffer<'a> {
72    buffer: &'a mut [u8],
73    offset: usize,
74    position: usize,
75}
76
77impl<'a> EncoderBuffer<'a> {
78    /// Creates a new encoder buffer.
79    ///
80    /// # Arguments
81    /// * `buffer` - Mutable byte buffer to write to
82    /// * `offset` - Starting offset in the buffer
83    #[must_use]
84    pub fn new(buffer: &'a mut [u8], offset: usize) -> Self {
85        Self {
86            buffer,
87            offset,
88            position: offset,
89        }
90    }
91
92    /// Returns the underlying buffer.
93    #[must_use]
94    pub fn buffer(&self) -> &[u8] {
95        self.buffer
96    }
97
98    /// Returns the mutable underlying buffer.
99    pub fn buffer_mut(&mut self) -> &mut [u8] {
100        self.buffer
101    }
102
103    /// Returns the starting offset.
104    #[must_use]
105    pub const fn offset(&self) -> usize {
106        self.offset
107    }
108
109    /// Returns the current write position.
110    #[must_use]
111    pub const fn position(&self) -> usize {
112        self.position
113    }
114
115    /// Returns the number of bytes written.
116    #[must_use]
117    pub const fn bytes_written(&self) -> usize {
118        self.position - self.offset
119    }
120
121    /// Returns the remaining capacity.
122    #[must_use]
123    pub fn remaining(&self) -> usize {
124        self.buffer.len() - self.position
125    }
126
127    /// Advances the position by the given number of bytes.
128    ///
129    /// # Arguments
130    /// * `count` - Number of bytes to advance
131    pub fn advance(&mut self, count: usize) {
132        self.position += count;
133    }
134
135    /// Sets the position to a specific offset.
136    ///
137    /// # Arguments
138    /// * `pos` - New position
139    pub fn set_position(&mut self, pos: usize) {
140        self.position = pos;
141    }
142
143    /// Writes a u8 at the current position and advances.
144    pub fn write_u8(&mut self, value: u8) {
145        self.buffer[self.position] = value;
146        self.position += 1;
147    }
148
149    /// Writes a u16 in little-endian at the current position and advances.
150    pub fn write_u16_le(&mut self, value: u16) {
151        let bytes = value.to_le_bytes();
152        self.buffer[self.position..self.position + 2].copy_from_slice(&bytes);
153        self.position += 2;
154    }
155
156    /// Writes a u32 in little-endian at the current position and advances.
157    pub fn write_u32_le(&mut self, value: u32) {
158        let bytes = value.to_le_bytes();
159        self.buffer[self.position..self.position + 4].copy_from_slice(&bytes);
160        self.position += 4;
161    }
162
163    /// Writes a u64 in little-endian at the current position and advances.
164    pub fn write_u64_le(&mut self, value: u64) {
165        let bytes = value.to_le_bytes();
166        self.buffer[self.position..self.position + 8].copy_from_slice(&bytes);
167        self.position += 8;
168    }
169
170    /// Writes bytes at the current position and advances.
171    pub fn write_bytes(&mut self, data: &[u8]) {
172        self.buffer[self.position..self.position + data.len()].copy_from_slice(data);
173        self.position += data.len();
174    }
175
176    /// Writes zeros at the current position and advances.
177    pub fn write_zeros(&mut self, count: usize) {
178        self.buffer[self.position..self.position + count].fill(0);
179        self.position += count;
180    }
181}
182
183/// Trait for group encoders.
184///
185/// Group encoders handle the encoding of repeating groups within messages.
186pub trait GroupEncoder {
187    /// The entry encoder type for this group.
188    type Entry<'a>
189    where
190        Self: 'a;
191
192    /// Returns the number of entries that will be written.
193    fn count(&self) -> u16;
194
195    /// Begins encoding a new entry.
196    ///
197    /// # Returns
198    /// An entry encoder for the new entry.
199    fn next_entry(&mut self) -> Option<Self::Entry<'_>>;
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn test_encoder_buffer_basic() {
208        let mut buf = [0u8; 64];
209        let mut encoder = EncoderBuffer::new(&mut buf, 0);
210
211        assert_eq!(encoder.offset(), 0);
212        assert_eq!(encoder.position(), 0);
213        assert_eq!(encoder.bytes_written(), 0);
214        assert_eq!(encoder.remaining(), 64);
215
216        encoder.write_u8(0xFF);
217        assert_eq!(encoder.position(), 1);
218        assert_eq!(encoder.bytes_written(), 1);
219
220        encoder.write_u16_le(0x1234);
221        assert_eq!(encoder.position(), 3);
222
223        encoder.write_u32_le(0xDEADBEEF);
224        assert_eq!(encoder.position(), 7);
225
226        encoder.write_u64_le(0x123456789ABCDEF0);
227        assert_eq!(encoder.position(), 15);
228    }
229
230    #[test]
231    fn test_encoder_buffer_with_offset() {
232        let mut buf = [0u8; 64];
233        let mut encoder = EncoderBuffer::new(&mut buf, 8);
234
235        assert_eq!(encoder.offset(), 8);
236        assert_eq!(encoder.position(), 8);
237        assert_eq!(encoder.bytes_written(), 0);
238
239        encoder.write_u32_le(0x12345678);
240        assert_eq!(encoder.position(), 12);
241        assert_eq!(encoder.bytes_written(), 4);
242
243        // Verify data was written at correct offset
244        assert_eq!(buf[8], 0x78);
245        assert_eq!(buf[9], 0x56);
246        assert_eq!(buf[10], 0x34);
247        assert_eq!(buf[11], 0x12);
248    }
249
250    #[test]
251    fn test_encoder_buffer_write_bytes() {
252        let mut buf = [0u8; 64];
253        let mut encoder = EncoderBuffer::new(&mut buf, 0);
254
255        encoder.write_bytes(b"Hello");
256        assert_eq!(encoder.position(), 5);
257        assert_eq!(&buf[0..5], b"Hello");
258    }
259
260    #[test]
261    fn test_encoder_buffer_write_zeros() {
262        let mut buf = [0xFFu8; 64];
263        let mut encoder = EncoderBuffer::new(&mut buf, 0);
264
265        encoder.write_zeros(8);
266        assert_eq!(encoder.position(), 8);
267        assert!(buf[0..8].iter().all(|&b| b == 0));
268        assert_eq!(buf[8], 0xFF);
269    }
270}