pub const STRING_FIELD_SIZE: usize = 6;
pub const STRING_FIELD_NONE_OFFSET: u32 = u32::MAX;
pub const STRING_FIELD_NONE_LENGTH: u16 = 0;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StringField {
pub offset: u32,
pub length: u16,
}
impl StringField {
pub const NONE: Self = Self {
offset: STRING_FIELD_NONE_OFFSET,
length: STRING_FIELD_NONE_LENGTH,
};
#[inline]
#[must_use]
pub const fn new(offset: u32, length: u16) -> Self {
Self { offset, length }
}
#[inline]
#[must_use]
pub const fn is_none(self) -> bool {
self.offset == STRING_FIELD_NONE_OFFSET && self.length == STRING_FIELD_NONE_LENGTH
}
#[inline]
pub fn write_le(self, dst: &mut [u8]) {
debug_assert!(
dst.len() >= STRING_FIELD_SIZE,
"StringField::write_le slice too short ({} bytes)",
dst.len()
);
dst[0..4].copy_from_slice(&self.offset.to_le_bytes());
dst[4..6].copy_from_slice(&self.length.to_le_bytes());
}
#[inline]
#[must_use]
pub fn read_le(src: &[u8]) -> Self {
debug_assert!(
src.len() >= STRING_FIELD_SIZE,
"StringField::read_le slice too short ({} bytes)",
src.len()
);
let offset = u32::from_le_bytes([src[0], src[1], src[2], src[3]]);
let length = u16::from_le_bytes([src[4], src[5]]);
Self { offset, length }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size_is_6_bytes() {
assert_eq!(STRING_FIELD_SIZE, 6);
}
#[test]
fn round_trip_through_write_read() {
let cases = [
StringField::new(0, 0),
StringField::new(1, 5),
StringField::new(0x1234_5678, 0xABCD),
StringField::new(u32::MAX - 1, u16::MAX),
StringField::NONE,
];
let mut buf = [0u8; STRING_FIELD_SIZE];
for field in cases {
buf.fill(0);
field.write_le(&mut buf);
let decoded = StringField::read_le(&buf);
assert_eq!(decoded, field, "round trip {field:?}");
}
}
#[test]
fn none_sentinel_distinct_from_empty_string() {
let none = StringField::NONE;
assert!(none.is_none());
let empty = StringField::new(0, 0);
assert!(
!empty.is_none(),
"empty (offset=0, length=0) is a valid string, not NONE"
);
}
#[test]
fn write_le_lays_out_little_endian() {
let field = StringField::new(0x0403_0201, 0x0605);
let mut buf = [0u8; STRING_FIELD_SIZE];
field.write_le(&mut buf);
assert_eq!(buf, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
}
}