324 lines
9.8 KiB
Rust
324 lines
9.8 KiB
Rust
|
|
use super::{BidError, BidResult, BidHandle, BidTag, BID_VERSION};
|
||
|
|
use std::mem;
|
||
|
|
|
||
|
|
/// BID-1 TLV Header
|
||
|
|
#[repr(C)]
|
||
|
|
#[derive(Debug, Clone, Copy)]
|
||
|
|
pub struct BidTlvHeader {
|
||
|
|
pub version: u16, // BID version (1)
|
||
|
|
pub argc: u16, // Argument count
|
||
|
|
}
|
||
|
|
|
||
|
|
/// TLV Entry structure
|
||
|
|
#[repr(C)]
|
||
|
|
#[derive(Debug, Clone, Copy)]
|
||
|
|
pub struct TlvEntry {
|
||
|
|
pub tag: u8, // Type tag
|
||
|
|
pub reserved: u8, // Reserved for future use (0)
|
||
|
|
pub size: u16, // Payload size
|
||
|
|
// Payload follows immediately after
|
||
|
|
}
|
||
|
|
|
||
|
|
/// TLV encoder for BID-1 format
|
||
|
|
pub struct TlvEncoder {
|
||
|
|
buffer: Vec<u8>,
|
||
|
|
entry_count: u16,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl TlvEncoder {
|
||
|
|
/// Create a new TLV encoder
|
||
|
|
pub fn new() -> Self {
|
||
|
|
let mut encoder = Self {
|
||
|
|
buffer: Vec::with_capacity(256),
|
||
|
|
entry_count: 0,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Reserve space for header
|
||
|
|
encoder.buffer.extend_from_slice(&[0; mem::size_of::<BidTlvHeader>()]);
|
||
|
|
encoder
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode a boolean value
|
||
|
|
pub fn encode_bool(&mut self, value: bool) -> BidResult<()> {
|
||
|
|
self.encode_entry(BidTag::Bool, &[if value { 1 } else { 0 }])
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode a 32-bit integer
|
||
|
|
pub fn encode_i32(&mut self, value: i32) -> BidResult<()> {
|
||
|
|
self.encode_entry(BidTag::I32, &value.to_le_bytes())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode a 64-bit integer
|
||
|
|
pub fn encode_i64(&mut self, value: i64) -> BidResult<()> {
|
||
|
|
self.encode_entry(BidTag::I64, &value.to_le_bytes())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode a 32-bit float
|
||
|
|
pub fn encode_f32(&mut self, value: f32) -> BidResult<()> {
|
||
|
|
self.encode_entry(BidTag::F32, &value.to_le_bytes())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode a 64-bit float
|
||
|
|
pub fn encode_f64(&mut self, value: f64) -> BidResult<()> {
|
||
|
|
self.encode_entry(BidTag::F64, &value.to_le_bytes())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode a string
|
||
|
|
pub fn encode_string(&mut self, value: &str) -> BidResult<()> {
|
||
|
|
let bytes = value.as_bytes();
|
||
|
|
if bytes.len() > u16::MAX as usize {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
self.encode_entry(BidTag::String, bytes)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode binary data
|
||
|
|
pub fn encode_bytes(&mut self, value: &[u8]) -> BidResult<()> {
|
||
|
|
if value.len() > u16::MAX as usize {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
self.encode_entry(BidTag::Bytes, value)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode a handle
|
||
|
|
pub fn encode_handle(&mut self, handle: BidHandle) -> BidResult<()> {
|
||
|
|
self.encode_entry(BidTag::Handle, &handle.to_u64().to_le_bytes())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Encode void (no payload)
|
||
|
|
pub fn encode_void(&mut self) -> BidResult<()> {
|
||
|
|
self.encode_entry(BidTag::Void, &[])
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Internal: encode a TLV entry
|
||
|
|
fn encode_entry(&mut self, tag: BidTag, payload: &[u8]) -> BidResult<()> {
|
||
|
|
let entry = TlvEntry {
|
||
|
|
tag: tag as u8,
|
||
|
|
reserved: 0,
|
||
|
|
size: payload.len() as u16,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Write entry header
|
||
|
|
self.buffer.push(entry.tag);
|
||
|
|
self.buffer.push(entry.reserved);
|
||
|
|
self.buffer.extend_from_slice(&entry.size.to_le_bytes());
|
||
|
|
|
||
|
|
// Write payload
|
||
|
|
self.buffer.extend_from_slice(payload);
|
||
|
|
|
||
|
|
self.entry_count += 1;
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Finalize the encoding and return the buffer
|
||
|
|
pub fn finish(mut self) -> Vec<u8> {
|
||
|
|
// Update header
|
||
|
|
let header = BidTlvHeader {
|
||
|
|
version: BID_VERSION,
|
||
|
|
argc: self.entry_count,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Write header at the beginning
|
||
|
|
self.buffer[0..2].copy_from_slice(&header.version.to_le_bytes());
|
||
|
|
self.buffer[2..4].copy_from_slice(&header.argc.to_le_bytes());
|
||
|
|
self.buffer
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// TLV decoder for BID-1 format
|
||
|
|
pub struct TlvDecoder<'a> {
|
||
|
|
data: &'a [u8],
|
||
|
|
position: usize,
|
||
|
|
header: BidTlvHeader,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<'a> TlvDecoder<'a> {
|
||
|
|
/// Create a new TLV decoder
|
||
|
|
pub fn new(data: &'a [u8]) -> BidResult<Self> {
|
||
|
|
if data.len() < mem::size_of::<BidTlvHeader>() {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read header safely
|
||
|
|
let version = u16::from_le_bytes([data[0], data[1]]);
|
||
|
|
let argc = u16::from_le_bytes([data[2], data[3]]);
|
||
|
|
let header = BidTlvHeader { version, argc };
|
||
|
|
|
||
|
|
if header.version != BID_VERSION {
|
||
|
|
return Err(BidError::VersionMismatch);
|
||
|
|
}
|
||
|
|
|
||
|
|
Ok(Self {
|
||
|
|
data,
|
||
|
|
position: mem::size_of::<BidTlvHeader>(),
|
||
|
|
header,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get the argument count
|
||
|
|
pub fn arg_count(&self) -> u16 {
|
||
|
|
self.header.argc
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode the next entry
|
||
|
|
pub fn decode_next(&mut self) -> BidResult<Option<(BidTag, &'a [u8])>> {
|
||
|
|
if self.position >= self.data.len() {
|
||
|
|
return Ok(None);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read entry header
|
||
|
|
if self.position + mem::size_of::<TlvEntry>() > self.data.len() {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read entry safely
|
||
|
|
let tag = self.data[self.position];
|
||
|
|
let reserved = self.data[self.position + 1];
|
||
|
|
let size = u16::from_le_bytes([
|
||
|
|
self.data[self.position + 2],
|
||
|
|
self.data[self.position + 3],
|
||
|
|
]);
|
||
|
|
let entry = TlvEntry { tag, reserved, size };
|
||
|
|
self.position += mem::size_of::<TlvEntry>();
|
||
|
|
|
||
|
|
// Read payload
|
||
|
|
let payload_end = self.position + entry.size as usize;
|
||
|
|
if payload_end > self.data.len() {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
|
||
|
|
let payload = &self.data[self.position..payload_end];
|
||
|
|
self.position = payload_end;
|
||
|
|
|
||
|
|
// Convert tag
|
||
|
|
let tag = match entry.tag {
|
||
|
|
1 => BidTag::Bool,
|
||
|
|
2 => BidTag::I32,
|
||
|
|
3 => BidTag::I64,
|
||
|
|
4 => BidTag::F32,
|
||
|
|
5 => BidTag::F64,
|
||
|
|
6 => BidTag::String,
|
||
|
|
7 => BidTag::Bytes,
|
||
|
|
8 => BidTag::Handle,
|
||
|
|
9 => BidTag::Void,
|
||
|
|
20 => BidTag::Result,
|
||
|
|
21 => BidTag::Option,
|
||
|
|
22 => BidTag::Array,
|
||
|
|
_ => return Err(BidError::InvalidType),
|
||
|
|
};
|
||
|
|
|
||
|
|
Ok(Some((tag, payload)))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode a boolean from payload
|
||
|
|
pub fn decode_bool(payload: &[u8]) -> BidResult<bool> {
|
||
|
|
if payload.len() != 1 {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
Ok(payload[0] != 0)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode an i32 from payload
|
||
|
|
pub fn decode_i32(payload: &[u8]) -> BidResult<i32> {
|
||
|
|
if payload.len() != 4 {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
Ok(i32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode an i64 from payload
|
||
|
|
pub fn decode_i64(payload: &[u8]) -> BidResult<i64> {
|
||
|
|
if payload.len() != 8 {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
let mut bytes = [0u8; 8];
|
||
|
|
bytes.copy_from_slice(payload);
|
||
|
|
Ok(i64::from_le_bytes(bytes))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode a handle from payload
|
||
|
|
pub fn decode_handle(payload: &[u8]) -> BidResult<BidHandle> {
|
||
|
|
if payload.len() != 8 {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
let mut bytes = [0u8; 8];
|
||
|
|
bytes.copy_from_slice(payload);
|
||
|
|
Ok(BidHandle::from_u64(u64::from_le_bytes(bytes)))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode an f32 from payload
|
||
|
|
pub fn decode_f32(payload: &[u8]) -> BidResult<f32> {
|
||
|
|
if payload.len() != 4 {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
Ok(f32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode an f64 from payload
|
||
|
|
pub fn decode_f64(payload: &[u8]) -> BidResult<f64> {
|
||
|
|
if payload.len() != 8 {
|
||
|
|
return Err(BidError::InvalidArgs);
|
||
|
|
}
|
||
|
|
let mut bytes = [0u8; 8];
|
||
|
|
bytes.copy_from_slice(payload);
|
||
|
|
Ok(f64::from_le_bytes(bytes))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode a string from payload
|
||
|
|
pub fn decode_string(payload: &[u8]) -> BidResult<&str> {
|
||
|
|
std::str::from_utf8(payload).map_err(|_| BidError::InvalidUtf8)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_encode_decode_primitives() {
|
||
|
|
let mut encoder = TlvEncoder::new();
|
||
|
|
encoder.encode_bool(true).unwrap();
|
||
|
|
encoder.encode_i32(42).unwrap();
|
||
|
|
encoder.encode_i64(9876543210).unwrap();
|
||
|
|
encoder.encode_string("Hello Nyash!").unwrap();
|
||
|
|
|
||
|
|
let data = encoder.finish();
|
||
|
|
let mut decoder = TlvDecoder::new(&data).unwrap();
|
||
|
|
|
||
|
|
assert_eq!(decoder.arg_count(), 4);
|
||
|
|
|
||
|
|
// Decode bool
|
||
|
|
let (tag, payload) = decoder.decode_next().unwrap().unwrap();
|
||
|
|
assert_eq!(tag, BidTag::Bool);
|
||
|
|
assert_eq!(TlvDecoder::decode_bool(payload).unwrap(), true);
|
||
|
|
|
||
|
|
// Decode i32
|
||
|
|
let (tag, payload) = decoder.decode_next().unwrap().unwrap();
|
||
|
|
assert_eq!(tag, BidTag::I32);
|
||
|
|
assert_eq!(TlvDecoder::decode_i32(payload).unwrap(), 42);
|
||
|
|
|
||
|
|
// Decode i64
|
||
|
|
let (tag, payload) = decoder.decode_next().unwrap().unwrap();
|
||
|
|
assert_eq!(tag, BidTag::I64);
|
||
|
|
assert_eq!(TlvDecoder::decode_i64(payload).unwrap(), 9876543210);
|
||
|
|
|
||
|
|
// Decode string
|
||
|
|
let (tag, payload) = decoder.decode_next().unwrap().unwrap();
|
||
|
|
assert_eq!(tag, BidTag::String);
|
||
|
|
assert_eq!(TlvDecoder::decode_string(payload).unwrap(), "Hello Nyash!");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_encode_decode_handle() {
|
||
|
|
let mut encoder = TlvEncoder::new();
|
||
|
|
let handle = BidHandle::new(6, 12345);
|
||
|
|
encoder.encode_handle(handle).unwrap();
|
||
|
|
|
||
|
|
let data = encoder.finish();
|
||
|
|
let mut decoder = TlvDecoder::new(&data).unwrap();
|
||
|
|
|
||
|
|
let (tag, payload) = decoder.decode_next().unwrap().unwrap();
|
||
|
|
assert_eq!(tag, BidTag::Handle);
|
||
|
|
assert_eq!(TlvDecoder::decode_handle(payload).unwrap(), handle);
|
||
|
|
}
|
||
|
|
}
|