serializer/machine/
slot.rs1use std::fmt;
7
8pub const MAX_INLINE_SIZE: usize = 14;
10
11pub const INLINE_MARKER: u8 = 0x00;
13
14pub const HEAP_MARKER: u8 = 0xFF;
16
17#[repr(C, packed)]
19#[derive(Clone, Copy)]
20pub struct DxMachineSlot {
21 pub data: [u8; 16],
36}
37
38impl DxMachineSlot {
39 #[inline]
41 pub const fn new() -> Self {
42 Self { data: [0; 16] }
43 }
44
45 #[inline]
47 pub fn inline_from_bytes(bytes: &[u8]) -> Result<Self, SlotError> {
48 let len = bytes.len();
49 if len > MAX_INLINE_SIZE {
50 return Err(SlotError::DataTooLarge {
51 size: len,
52 max: MAX_INLINE_SIZE,
53 });
54 }
55
56 let mut slot = Self::new();
57 slot.data[0] = len as u8;
58 slot.data[1..1 + len].copy_from_slice(bytes);
59 slot.data[15] = INLINE_MARKER;
60
61 Ok(slot)
62 }
63
64 #[inline]
66 pub fn heap_reference(offset: u32, length: u32) -> Self {
67 let mut slot = Self::new();
68 slot.data[0..4].copy_from_slice(&offset.to_le_bytes());
69 slot.data[4..8].copy_from_slice(&length.to_le_bytes());
70 slot.data[15] = HEAP_MARKER;
71 slot
72 }
73
74 #[inline]
76 pub const fn is_inline(&self) -> bool {
77 self.data[15] == INLINE_MARKER
78 }
79
80 #[inline]
82 pub const fn is_heap(&self) -> bool {
83 self.data[15] == HEAP_MARKER
84 }
85
86 #[inline]
88 pub const fn inline_len(&self) -> usize {
89 debug_assert!(self.is_inline());
90 self.data[0] as usize
91 }
92
93 #[inline]
95 pub fn inline_data(&self) -> &[u8] {
96 debug_assert!(self.is_inline());
97 let len = self.inline_len();
98 &self.data[1..1 + len]
99 }
100
101 #[inline]
108 #[allow(clippy::expect_used)] pub fn inline_str(&self) -> &str {
110 debug_assert!(self.is_inline());
111 let data = self.inline_data();
112 std::str::from_utf8(data).expect("Invalid UTF-8 in inline data")
116 }
117
118 #[inline]
122 pub fn inline_str_checked(&self) -> Option<&str> {
123 if !self.is_inline() {
124 return None;
125 }
126 let data = self.inline_data();
127 std::str::from_utf8(data).ok()
128 }
129
130 #[inline]
132 pub fn heap_offset(&self) -> u32 {
133 debug_assert!(self.is_heap());
134 u32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]])
135 }
136
137 #[inline]
139 pub fn heap_length(&self) -> u32 {
140 debug_assert!(self.is_heap());
141 u32::from_le_bytes([self.data[4], self.data[5], self.data[6], self.data[7]])
142 }
143
144 #[inline]
146 pub fn heap_ref(&self) -> (u32, u32) {
147 debug_assert!(self.is_heap());
148 (self.heap_offset(), self.heap_length())
149 }
150
151 #[inline]
153 pub fn write_inline(&mut self, bytes: &[u8]) -> Result<(), SlotError> {
154 let len = bytes.len();
155 if len > MAX_INLINE_SIZE {
156 return Err(SlotError::DataTooLarge {
157 size: len,
158 max: MAX_INLINE_SIZE,
159 });
160 }
161
162 self.data[0] = len as u8;
163 self.data[1..1 + len].copy_from_slice(bytes);
164 self.data[15] = INLINE_MARKER;
165
166 Ok(())
167 }
168
169 #[inline]
171 pub fn write_heap(&mut self, offset: u32, length: u32) {
172 self.data[0..4].copy_from_slice(&offset.to_le_bytes());
173 self.data[4..8].copy_from_slice(&length.to_le_bytes());
174 self.data[8..15].fill(0);
175 self.data[15] = HEAP_MARKER;
176 }
177
178 #[inline]
180 pub fn eq_inline_bytes(&self, other: &[u8]) -> bool {
181 if !self.is_inline() {
182 return false;
183 }
184
185 let len = self.inline_len();
186 if len != other.len() {
187 return false;
188 }
189
190 self.inline_data() == other
191 }
192
193 #[inline]
195 pub fn eq_inline_str(&self, other: &str) -> bool {
196 self.eq_inline_bytes(other.as_bytes())
197 }
198
199 #[inline]
201 pub fn size(&self) -> usize {
202 if self.is_inline() {
203 self.inline_len()
204 } else {
205 self.heap_length() as usize
206 }
207 }
208}
209
210impl Default for DxMachineSlot {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216impl fmt::Debug for DxMachineSlot {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 if self.is_inline() {
219 let len = self.inline_len();
220 let data = self.inline_data();
221 if let Ok(s) = std::str::from_utf8(data) {
222 f.debug_struct("DxMachineSlot")
223 .field("type", &"inline")
224 .field("len", &len)
225 .field("data", &s)
226 .finish()
227 } else {
228 f.debug_struct("DxMachineSlot")
229 .field("type", &"inline")
230 .field("len", &len)
231 .field("data", &data)
232 .finish()
233 }
234 } else if self.is_heap() {
235 f.debug_struct("DxMachineSlot")
236 .field("type", &"heap")
237 .field("offset", &self.heap_offset())
238 .field("length", &self.heap_length())
239 .finish()
240 } else {
241 f.debug_struct("DxMachineSlot")
242 .field("type", &"invalid")
243 .field("marker", &self.data[15])
244 .finish()
245 }
246 }
247}
248
249#[derive(Debug, Clone, PartialEq, Eq)]
251pub enum SlotError {
252 DataTooLarge {
254 size: usize,
256 max: usize,
258 },
259 InvalidMarker {
261 found: u8,
263 },
264}
265
266impl fmt::Display for SlotError {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 match self {
269 Self::DataTooLarge { size, max } => {
270 write!(f, "Data size {} exceeds max inline size {}", size, max)
271 }
272 Self::InvalidMarker { found } => {
273 write!(f, "Invalid slot marker byte: 0x{:02X}", found)
274 }
275 }
276 }
277}
278
279impl std::error::Error for SlotError {}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn test_slot_inline() {
287 let data = b"Hello, World!"; let slot = DxMachineSlot::inline_from_bytes(data).unwrap();
289
290 assert!(slot.is_inline());
291 assert!(!slot.is_heap());
292 assert_eq!(slot.inline_len(), 13);
293 assert_eq!(slot.inline_data(), data);
294 assert_eq!(slot.inline_str(), "Hello, World!");
295 assert_eq!(slot.size(), 13);
296 }
297
298 #[test]
299 fn test_slot_heap() {
300 let slot = DxMachineSlot::heap_reference(100, 500);
301
302 assert!(slot.is_heap());
303 assert!(!slot.is_inline());
304 assert_eq!(slot.heap_offset(), 100);
305 assert_eq!(slot.heap_length(), 500);
306 assert_eq!(slot.heap_ref(), (100, 500));
307 assert_eq!(slot.size(), 500);
308 }
309
310 #[test]
311 fn test_slot_max_inline() {
312 let data = b"12345678901234"; let slot = DxMachineSlot::inline_from_bytes(data).unwrap();
314
315 assert!(slot.is_inline());
316 assert_eq!(slot.inline_len(), 14);
317 assert_eq!(slot.inline_data(), data);
318 }
319
320 #[test]
321 fn test_slot_too_large() {
322 let data = b"123456789012345"; let result = DxMachineSlot::inline_from_bytes(data);
324
325 assert!(matches!(
326 result,
327 Err(SlotError::DataTooLarge { size: 15, max: 14 })
328 ));
329 }
330
331 #[test]
332 fn test_slot_write_inline() {
333 let mut slot = DxMachineSlot::new();
334 slot.write_inline(b"Test").unwrap();
335
336 assert!(slot.is_inline());
337 assert_eq!(slot.inline_str(), "Test");
338 }
339
340 #[test]
341 fn test_slot_write_heap() {
342 let mut slot = DxMachineSlot::new();
343 slot.write_heap(42, 1000);
344
345 assert!(slot.is_heap());
346 assert_eq!(slot.heap_offset(), 42);
347 assert_eq!(slot.heap_length(), 1000);
348 }
349
350 #[test]
351 fn test_slot_eq() {
352 let slot = DxMachineSlot::inline_from_bytes(b"test").unwrap();
353
354 assert!(slot.eq_inline_bytes(b"test"));
355 assert!(!slot.eq_inline_bytes(b"Test"));
356 assert!(!slot.eq_inline_bytes(b"testing"));
357
358 assert!(slot.eq_inline_str("test"));
359 assert!(!slot.eq_inline_str("Test"));
360 }
361
362 #[test]
363 fn test_slot_size() {
364 assert_eq!(std::mem::size_of::<DxMachineSlot>(), 16);
365 }
366}