use crate::error::TsonError;
use crate::structure::*;
use alloc::{format, string::String, vec::Vec};
const MAX_RECURSION_DEPTH: u8 = 128;
const MAX_DEFINITIONS: usize = 2048;
const MAX_FIELDS_PER_OBJECT: usize = 256;
const MAX_DATA_ENTRIES: usize = 1_048_576;
const MIN_DEF_BYTES: usize = 3;
const DATA_ENTRY_HEADER_BYTES: usize = 6;
const STRREF_SENTINEL: u8 = 0xFF;
const LONG_TAG: u8 = 0xFE;
#[inline]
fn str_from_utf8(slice: &[u8], ctx: &str) -> Result<String, TsonError> {
core::str::from_utf8(slice)
.map(String::from)
.map_err(|e| TsonError::ParseError(format!("Invalid UTF-8 {}: {}", ctx, e)))
}
fn decode_str_payload(bytes: &[u8]) -> Result<(TsonData, usize), TsonError> {
if bytes.is_empty() {
return Err(TsonError::ParseError("Expected string/StrRef byte".into()));
}
let first = bytes[0];
if first <= 0x7F {
let len = first as usize;
if bytes.len() < 1 + len {
return Err(TsonError::ParseError(format!(
"Truncated short string: need {} bytes, got {}",
1 + len,
bytes.len()
)));
}
let s = str_from_utf8(&bytes[1..1 + len], "string")?;
Ok((TsonData::String(s), 1 + len))
} else if first < 0xFE {
if bytes.len() < 2 {
return Err(TsonError::ParseError(
"Truncated medium string length".into(),
));
}
let len = ((first as usize & 0x3F) << 8) | bytes[1] as usize;
if bytes.len() < 2 + len {
return Err(TsonError::ParseError(format!(
"Truncated medium string: need {} bytes, got {}",
2 + len,
bytes.len()
)));
}
let s = str_from_utf8(&bytes[2..2 + len], "string")?;
Ok((TsonData::String(s), 2 + len))
} else if first == LONG_TAG {
if bytes.len() < 4 {
return Err(TsonError::ParseError("Truncated long string length".into()));
}
let mut u24_buf = [0u8; 4];
u24_buf[..3].copy_from_slice(&bytes[1..4]);
let len = u32::from_le_bytes(u24_buf) as usize;
if bytes.len() < 4 + len {
return Err(TsonError::ParseError(format!(
"Truncated long string: need {} bytes, got {}",
4 + len,
bytes.len()
)));
}
let s = str_from_utf8(&bytes[4..4 + len], "string")?;
Ok((TsonData::String(s), 4 + len))
} else {
if bytes.len() < 5 {
return Err(TsonError::ParseError("Truncated StrRef dict index".into()));
}
let idx = u32::from_le_bytes(bytes[1..5].try_into().unwrap());
Ok((TsonData::StrRef(idx), 5))
}
}
fn decode_dict_str(bytes: &[u8]) -> Result<(String, usize), TsonError> {
if bytes.is_empty() {
return Err(TsonError::ParseError("Truncated dict string".into()));
}
let first = bytes[0];
if first == STRREF_SENTINEL || (0xC0..0xFE).contains(&first) {
return Err(TsonError::ParseError(format!(
"Invalid dict string encoding byte: 0x{:02X}",
first
)));
}
let (len, head_len) = if first <= 0x7F {
(first as usize, 1)
} else if first < 0xFE {
if bytes.len() < 2 {
return Err(TsonError::ParseError("Truncated medium dict string".into()));
}
(((first as usize & 0x3F) << 8) | bytes[1] as usize, 2)
} else
{
if bytes.len() < 4 {
return Err(TsonError::ParseError("Truncated long dict string".into()));
}
let mut u24 = [0u8; 4];
u24[..3].copy_from_slice(&bytes[1..4]);
(u32::from_le_bytes(u24) as usize, 4)
};
if bytes.len() < head_len + len {
return Err(TsonError::ParseError("Truncated dict string data".into()));
}
let s = str_from_utf8(&bytes[head_len..head_len + len], "in dict")?;
Ok((s, head_len + len))
}
pub fn decode_document(bytes: &[u8]) -> Result<TsonDocument, TsonError> {
let header = TsonHeader::from_bytes(bytes)?;
header.validate()?;
let def_off = header.blk_definition as usize;
let dict_off = header.blk_dict as usize;
let data_off = header.blk_data as usize;
if data_off > bytes.len() {
return Err(TsonError::ParseError(format!(
"Data block offset {} exceeds buffer length {}",
data_off,
bytes.len()
)));
}
let definitions = decode_definitions_slice(&bytes[def_off..dict_off])?;
let dict = decode_dict_slice(&bytes[dict_off..data_off])?;
let data = decode_data_entries(&bytes[data_off..], &definitions)?;
Ok(TsonDocument {
header,
definitions,
dict,
data,
})
}
fn decode_definitions_slice(bytes: &[u8]) -> Result<Vec<TsonDefinition>, TsonError> {
if bytes.len() < 2 {
return Err(TsonError::ParseError(format!(
"Definition block too short: {} bytes",
bytes.len()
)));
}
let count = u16::from_le_bytes(bytes[0..2].try_into().unwrap()) as usize;
if count > MAX_DEFINITIONS {
return Err(TsonError::ParseError(format!(
"Definition count {} exceeds max {}",
count, MAX_DEFINITIONS
)));
}
let min_required = 2 + count * MIN_DEF_BYTES;
if bytes.len() < min_required {
return Err(TsonError::ParseError(format!(
"Definition block has {} bytes but {} defs require {}",
bytes.len(),
count,
min_required
)));
}
let mut defs = Vec::with_capacity(count);
let mut pos = 2usize;
for _ in 0..count {
if pos >= bytes.len() {
return Err(TsonError::ParseError(format!(
"Truncated definition block at entry {}",
defs.len()
)));
}
let def_type = TsonType::from_u8(bytes[pos])?;
pos += 1;
if pos + 2 > bytes.len() {
return Err(TsonError::ParseError("Truncated definition index".into()));
}
let index = u16::from_le_bytes(bytes[pos..pos + 2].try_into().unwrap());
pos += 2;
match def_type {
TsonType::Object => {
if pos + 2 > bytes.len() {
return Err(TsonError::ParseError("Truncated object field count".into()));
}
let field_count =
u16::from_le_bytes(bytes[pos..pos + 2].try_into().unwrap()) as usize;
pos += 2;
if field_count > MAX_FIELDS_PER_OBJECT {
return Err(TsonError::ParseError(format!(
"Object #{} has {} fields (max {})",
index, field_count, MAX_FIELDS_PER_OBJECT
)));
}
let mut fields = Vec::with_capacity(field_count);
for fi in 0..field_count {
if pos >= bytes.len() {
return Err(TsonError::ParseError(format!(
"Truncated field {} name length",
fi
)));
}
let name_len = bytes[pos] as usize;
pos += 1;
if pos + name_len + 1 > bytes.len() {
return Err(TsonError::ParseError(format!(
"Truncated field {} name data",
fi
)));
}
let name = str_from_utf8(&bytes[pos..pos + name_len], "in field name")?;
pos += name_len;
let field_type = TsonType::from_u8(bytes[pos])?;
pos += 1;
fields.push((name, field_type));
}
defs.push(TsonDefinition {
def_type,
index,
name: None,
fields: Some(fields),
elem_type: None,
});
}
TsonType::Array => {
if pos >= bytes.len() {
return Err(TsonError::ParseError("Truncated array elem_type".into()));
}
let elem_type = TsonType::from_u8(bytes[pos])?;
pos += 1;
defs.push(TsonDefinition {
def_type,
index,
name: None,
fields: None,
elem_type: Some(elem_type),
});
}
_ => {
defs.push(TsonDefinition {
def_type,
index,
name: None,
fields: None,
elem_type: None,
});
}
}
}
Ok(defs)
}
pub fn decode_definitions(bytes: &[u8]) -> Result<Vec<TsonDefinition>, TsonError> {
decode_definitions_slice(bytes)
}
pub(crate) fn decode_dict_slice(bytes: &[u8]) -> Result<Vec<String>, TsonError> {
if bytes.is_empty() {
return Ok(Vec::new());
}
if bytes.len() < 4 {
return Err(TsonError::ParseError(
"Dict block too short for entry count".into(),
));
}
let count = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as usize;
let mut dict = Vec::with_capacity(count);
let mut pos = 4usize;
for _ in 0..count {
let (s, consumed) = decode_dict_str(&bytes[pos..])?;
pos += consumed;
dict.push(s);
}
Ok(dict)
}
pub fn decode_data_entries(
bytes: &[u8],
defs: &[TsonDefinition],
) -> Result<Vec<TsonChunk>, TsonError> {
if bytes.len() < 4 {
return Err(TsonError::ParseError(format!(
"Data block too short: {} bytes",
bytes.len()
)));
}
let count = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as usize;
if count > MAX_DATA_ENTRIES {
return Err(TsonError::ParseError(format!(
"Data entry count {} exceeds max {}",
count, MAX_DATA_ENTRIES
)));
}
let mut chunks = Vec::with_capacity(count);
let mut pos = 4usize;
for i in 0..count {
if pos + DATA_ENTRY_HEADER_BYTES > bytes.len() {
return Err(TsonError::ParseError(format!(
"Truncated entry {} header",
i
)));
}
let def_index = u16::from_le_bytes(bytes[pos..pos + 2].try_into().unwrap());
let payload_len = u32::from_le_bytes(bytes[pos + 2..pos + 6].try_into().unwrap()) as usize;
pos += DATA_ENTRY_HEADER_BYTES;
if pos + payload_len > bytes.len() {
return Err(TsonError::ParseError(format!(
"Entry {} payload ({} bytes) exceeds buffer",
i, payload_len
)));
}
let payload = &bytes[pos..pos + payload_len];
let (data, consumed) = decode_root_value(payload, def_index, defs, 0)?;
if consumed != payload_len {
return Err(TsonError::ParseError(format!(
"Mismatch entry {}: consumed {} of {}",
i, consumed, payload_len
)));
}
chunks.push(TsonChunk {
definition_index: def_index,
data,
});
pos += payload_len;
}
Ok(chunks)
}
pub(crate) fn decode_root_value(
bytes: &[u8],
def_index: u16,
all_defs: &[TsonDefinition],
depth: u8,
) -> Result<(TsonData, usize), TsonError> {
let def = resolve_def(def_index, all_defs)?;
match def.def_type {
TsonType::Object => decode_object_value(bytes, all_defs, depth + 1),
TsonType::Array => decode_array_value(bytes, all_defs, depth + 1),
_ => decode_primitive_value(bytes, def.def_type),
}
}
fn decode_inline_value(
bytes: &[u8],
inline_type: TsonType,
all_defs: &[TsonDefinition],
depth: u8,
) -> Result<(TsonData, usize), TsonError> {
match inline_type {
TsonType::Object => decode_object_value(bytes, all_defs, depth + 1),
TsonType::Array => decode_array_value(bytes, all_defs, depth + 1),
_ => decode_primitive_value(bytes, inline_type),
}
}
fn decode_object_value(
bytes: &[u8],
all_defs: &[TsonDefinition],
depth: u8,
) -> Result<(TsonData, usize), TsonError> {
if depth > MAX_RECURSION_DEPTH {
return Err(TsonError::ParseError(format!(
"Max recursion depth {} exceeded at Object",
MAX_RECURSION_DEPTH
)));
}
if bytes.len() < 2 {
return Err(TsonError::ParseError(
"Truncated object self_def_index".into(),
));
}
let self_def = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
let def = resolve_def(self_def, all_defs)?;
let fields = def
.fields
.as_ref()
.ok_or_else(|| TsonError::ParseError(format!("Object #{} has no fields", self_def)))?;
let mut values = Vec::with_capacity(fields.len());
let mut pos = 2usize;
for (_field_name, field_type) in fields {
let (val, consumed) = decode_inline_value(&bytes[pos..], *field_type, all_defs, depth)?;
pos += consumed;
values.push(val);
}
Ok((TsonData::Object(self_def, values), pos))
}
fn decode_array_value(
bytes: &[u8],
all_defs: &[TsonDefinition],
depth: u8,
) -> Result<(TsonData, usize), TsonError> {
if depth > MAX_RECURSION_DEPTH {
return Err(TsonError::ParseError("Max recursion depth".into()));
}
if bytes.len() < 2 {
return Err(TsonError::ParseError(
"Truncated array self_def_index".into(),
));
}
let self_def = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
let def = resolve_def(self_def, all_defs)?;
let elem_type = def
.elem_type
.ok_or_else(|| TsonError::ParseError(format!("Array #{} has no elem_type", self_def)))?;
if bytes.len() < 4 {
return Err(TsonError::ParseError(
"Truncated array elem_def_index".into(),
));
}
let elem_def_index = u16::from_le_bytes(bytes[2..4].try_into().unwrap());
if bytes.len() < 6 {
return Err(TsonError::ParseError("Truncated array count".into()));
}
let count = u16::from_le_bytes(bytes[4..6].try_into().unwrap()) as usize;
let mut items = Vec::with_capacity(count);
let mut pos = 6usize;
for _ in 0..count {
let (item, consumed) = decode_inline_value(&bytes[pos..], elem_type, all_defs, depth)?;
pos += consumed;
items.push(item);
}
Ok((TsonData::Array(self_def, elem_def_index, items), pos))
}
fn decode_primitive_value(bytes: &[u8], ty: TsonType) -> Result<(TsonData, usize), TsonError> {
match ty {
TsonType::Null => Ok((TsonData::Null, 0)),
TsonType::Bool => {
if bytes.is_empty() {
return Err(TsonError::ParseError("Expected bool byte".into()));
}
Ok((TsonData::Bool(bytes[0] != 0), 1))
}
TsonType::Int => {
if bytes.len() < 4 {
return Err(TsonError::ParseError("Expected int32 (4 bytes)".into()));
}
Ok((
TsonData::Int(i32::from_le_bytes(bytes[0..4].try_into().unwrap())),
4,
))
}
TsonType::UInt => {
if bytes.len() < 4 {
return Err(TsonError::ParseError("Expected uint32 (4 bytes)".into()));
}
Ok((
TsonData::UInt(u32::from_le_bytes(bytes[0..4].try_into().unwrap())),
4,
))
}
TsonType::Float => {
if bytes.len() < 4 {
return Err(TsonError::ParseError("Expected float32 (4 bytes)".into()));
}
Ok((
TsonData::Float(f32::from_le_bytes(bytes[0..4].try_into().unwrap())),
4,
))
}
TsonType::String => decode_str_payload(bytes),
TsonType::Array | TsonType::Object => Err(TsonError::ParseError(
"Unexpected compound type in primitive decode path".into(),
)),
}
}
fn resolve_def(index: u16, all_defs: &[TsonDefinition]) -> Result<&TsonDefinition, TsonError> {
all_defs
.get(index as usize)
.ok_or_else(|| TsonError::ParseError(format!("Unknown definition index: {}", index)))
}