1use crate::binary::{Error, Reader, Result, Writer};
4use crate::records::{hex, unhex};
5use serde_json::{Value, json};
6
7#[derive(Debug, Clone, PartialEq)]
9pub struct Table {
10 pub types: Vec<u8>,
12 pub rows: Vec<Vec<Value>>,
14 pub trailing: Vec<u8>,
16}
17fn error(message: &str) -> Error {
18 Error {
19 offset: 0,
20 message: message.into(),
21 }
22}
23pub fn crypt_table(input: &[u8], encrypt: bool) -> Vec<u8> {
25 let mut key = 0x816_u32;
26 input
27 .iter()
28 .map(|&byte| {
29 let value = byte ^ (key >> 8) as u8;
30 let ciphertext = if encrypt { value } else { byte };
31 key = ((u32::from(ciphertext) + key) * 0x6081 + 0x1608) & 0xffff;
32 value
33 })
34 .collect()
35}
36fn validate(types: &[u8]) -> Result<()> {
37 if types.is_empty()
38 || types.len() > 1024
39 || types[0] != 6
40 || types.iter().any(|kind| !(1..=9).contains(kind))
41 {
42 return Err(error("Invalid TBL column schema"));
43 }
44 Ok(())
45}
46pub fn parse_table(input: &[u8]) -> Result<Table> {
48 if input.len() > 256 * 1024 * 1024 {
49 return Err(error("TBL byte budget exceeded"));
50 }
51 let bytes = crypt_table(input, false);
52 let mut reader = Reader::new(&bytes);
53 let columns = reader.count(1024, 4)?;
54 let mut types = Vec::with_capacity(columns);
55 for _ in 0..columns {
56 types.push(u8::try_from(reader.i32()?).map_err(|_| error("Invalid column type"))?);
57 }
58 validate(&types)?;
59 let minimum: usize = types
60 .iter()
61 .map(|&kind| [0, 1, 1, 2, 2, 4, 4, 4, 4, 8][kind as usize])
62 .sum();
63 let count = reader.count(8_000_000 / columns, minimum)?;
64 let mut rows = Vec::with_capacity(count);
65 for _ in 0..count {
66 let mut row = Vec::with_capacity(columns);
67 for kind in &types {
68 row.push(match kind {
69 1 => json!(reader.i8()?),
70 2 => json!(reader.u8()?),
71 3 => json!(reader.i16()?),
72 4 => json!(reader.u16()?),
73 5 => json!(reader.i32()?),
74 6 | 8 => json!(reader.u32()?),
75 7 => json!(hex(&reader.string(1_048_576)?)),
76 9 => json!(hex(reader.raw(8)?)),
77 _ => return Err(error("Unsupported TBL column")),
78 });
79 }
80 rows.push(row);
81 }
82 Ok(Table {
83 types,
84 rows,
85 trailing: reader.raw(reader.remaining())?.to_vec(),
86 })
87}
88pub fn write_table(table: &Table) -> Result<Vec<u8>> {
90 validate(&table.types)?;
91 if table.rows.len() > 8_000_000 / table.types.len() {
92 return Err(error("TBL row budget exceeded"));
93 }
94 let mut writer = Writer::default();
95 writer.i32(table.types.len() as i32)?;
96 for &kind in &table.types {
97 writer.i32(i32::from(kind))?;
98 }
99 writer.i32(table.rows.len() as i32)?;
100 for row in &table.rows {
101 if row.len() != table.types.len() {
102 return Err(error("TBL row width mismatch"));
103 }
104 for (&kind, cell) in table.types.iter().zip(row) {
105 if kind == 7 || kind == 9 {
106 let bytes = unhex(
107 cell.as_str()
108 .ok_or_else(|| error("Expected cell hex bytes"))?,
109 )?;
110 if kind == 7 {
111 writer.string(&bytes)?;
112 } else {
113 if bytes.len() != 8 {
114 return Err(error("f64 requires eight bytes"));
115 }
116 writer.raw(&bytes)?;
117 }
118 } else {
119 let value = cell
120 .as_i64()
121 .ok_or_else(|| error("Expected numeric cell"))?;
122 let invalid = |_| error("Cell outside column range");
123 match kind {
124 1 => writer.i8(i8::try_from(value).map_err(invalid)?)?,
125 2 => writer.u8(u8::try_from(value).map_err(invalid)?)?,
126 3 => writer.i16(i16::try_from(value).map_err(invalid)?)?,
127 4 => writer.u16(u16::try_from(value).map_err(invalid)?)?,
128 5 => writer.i32(i32::try_from(value).map_err(invalid)?)?,
129 6 | 8 => writer.u32(u32::try_from(value).map_err(invalid)?)?,
130 _ => return Err(error("Unsupported column")),
131 }
132 }
133 }
134 }
135 writer.raw(&table.trailing)?;
136 Ok(crypt_table(&writer.finish(), true))
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 #[test]
143 fn every_column_roundtrips() -> Result<()> {
144 let table = Table {
145 types: vec![6, 1, 2, 3, 4, 5, 7, 8, 9],
146 rows: vec![vec![
147 json!(4294967295_u32),
148 json!(-128),
149 json!(255),
150 json!(-32768),
151 json!(65535),
152 json!(-2147483648_i32),
153 json!("00ff0000"),
154 json!(0x7fa12345_u32),
155 json!("010000000000f07f"),
156 ]],
157 trailing: vec![255],
158 };
159 let bytes = write_table(&table)?;
160 assert_eq!(parse_table(&bytes)?, table);
161 assert_eq!(write_table(&parse_table(&bytes)?)?, bytes);
162 Ok(())
163 }
164}