llsd_rs/
binary.rs

1use std::io::{Read, Write};
2
3use chrono::{DateTime, Utc};
4use uuid::Uuid;
5
6use crate::{Llsd, Uri};
7
8fn write_inner<W: Write>(llsd: &Llsd, w: &mut W) -> Result<(), anyhow::Error> {
9    match llsd {
10        Llsd::Undefined => w.write_all(b"!")?,
11        Llsd::Boolean(v) => w.write_all(if *v { b"1" } else { b"0" })?,
12        Llsd::Integer(v) => {
13            w.write_all(b"i")?;
14            w.write_all(&v.to_be_bytes())?;
15        }
16        Llsd::Real(v) => {
17            w.write_all(b"r")?;
18            w.write_all(&v.to_be_bytes())?;
19        }
20        Llsd::String(v) => {
21            w.write_all(b"s")?;
22            w.write_all(&(v.len() as u32).to_be_bytes())?;
23            w.write_all(v.as_bytes())?;
24        }
25        Llsd::Uri(v) => {
26            w.write_all(b"l")?;
27            let v = v.as_str();
28            w.write_all(&(v.len() as u32).to_be_bytes())?;
29            w.write_all(v.as_bytes())?;
30        }
31        Llsd::Uuid(v) => {
32            w.write_all(b"u")?;
33            w.write_all((*v).as_bytes())?;
34        }
35        Llsd::Date(v) => {
36            w.write_all(b"d")?;
37            let real: f64 =
38                v.timestamp() as f64 + (v.timestamp_subsec_nanos() as f64 / 1_000_000_000.0);
39            // Use little endian
40            w.write_all(&real.to_le_bytes())?;
41        }
42        Llsd::Binary(v) => {
43            w.write_all(b"b")?;
44            w.write_all(&(v.len() as u32).to_be_bytes())?;
45            w.write_all(v)?;
46        }
47        Llsd::Array(v) => {
48            w.write_all(b"[")?;
49            w.write_all(&(v.len() as u32).to_be_bytes())?;
50            for e in v {
51                write_inner(e, w)?;
52            }
53            w.write_all(b"]")?;
54        }
55        Llsd::Map(v) => {
56            w.write_all(b"{")?;
57            w.write_all(&(v.len() as u32).to_be_bytes())?;
58            for (k, e) in v {
59                w.write_all(b"k")?;
60                w.write_all(&(k.len() as u32).to_be_bytes())?;
61                w.write_all(k.as_bytes())?;
62                write_inner(e, w)?;
63            }
64            w.write_all(b"}")?;
65        }
66    }
67    Ok(())
68}
69
70pub fn write<W: Write>(llsd: &Llsd, w: &mut W) -> Result<(), anyhow::Error> {
71    write_inner(llsd, w)
72}
73
74pub fn to_vec(llsd: &Llsd) -> Result<Vec<u8>, anyhow::Error> {
75    let mut buf = Vec::new();
76    write(llsd, &mut buf)?;
77    Ok(buf)
78}
79
80macro_rules! read_be_fn {
81    ($func_name:ident, $type:ty) => {
82        fn $func_name<R: Read>(reader: &mut R) -> Result<$type, anyhow::Error> {
83            let mut buf = [0_u8; std::mem::size_of::<$type>()];
84            reader.read_exact(&mut buf)?;
85            Ok(<$type>::from_be_bytes(buf))
86        }
87    };
88}
89
90read_be_fn!(read_u8, u8);
91read_be_fn!(read_i32_be, i32);
92read_be_fn!(read_f64_be, f64);
93
94fn hex<R: Read>(r: &mut R) -> Result<u8, anyhow::Error> {
95    let c = read_u8(r)?;
96    match c {
97        b'0'..=b'9' => Ok(c - b'0'),
98        b'a'..=b'f' => Ok(c - b'a' + 10),
99        b'A'..=b'F' => Ok(c - b'A' + 10),
100        _ => Ok(0),
101    }
102}
103
104fn unescape<R: Read>(r: &mut R, delim: u8) -> Result<String, anyhow::Error> {
105    let mut buf = Vec::new();
106    loop {
107        match read_u8(r)? {
108            c if c == delim => break,
109            b'\\' => match read_u8(r)? {
110                b'a' => buf.push(0x07),
111                b'b' => buf.push(0x08),
112                b'f' => buf.push(0x0c),
113                b'n' => buf.push(b'\n'),
114                b'r' => buf.push(b'\r'),
115                b't' => buf.push(b'\t'),
116                b'v' => buf.push(0x0b),
117                b'\\' => buf.push(b'\\'),
118                b'\'' => buf.push(b'\''),
119                b'"' => buf.push(b'"'),
120                b'x' => buf.push((hex(r)? << 4) | hex(r)?),
121                other => buf.push(other),
122            },
123            other => buf.push(other),
124        }
125    }
126    Ok(String::from_utf8(buf)?)
127}
128
129pub fn from_reader_inner<R: Read>(r: &mut R) -> Result<Llsd, anyhow::Error> {
130    match read_u8(r)? {
131        b'!' => Ok(Llsd::Undefined),
132        b'1' => Ok(Llsd::Boolean(true)),
133        b'0' => Ok(Llsd::Boolean(false)),
134        b'i' => Ok(Llsd::Integer(read_i32_be(r)?)),
135        b'r' => Ok(Llsd::Real(read_f64_be(r)?)),
136        b's' => {
137            let len = read_i32_be(r)? as usize;
138            let mut buf = vec![0; len];
139            r.read_exact(&mut buf)?;
140            Ok(Llsd::String(String::from_utf8(buf)?))
141        }
142        b'l' => {
143            let len = read_i32_be(r)? as usize;
144            let mut buf = vec![0; len];
145            r.read_exact(&mut buf)?;
146            Ok(Llsd::Uri(Uri::parse(std::str::from_utf8(&buf)?)))
147        }
148        b'u' => {
149            let mut buf = [0_u8; 16];
150            r.read_exact(&mut buf)?;
151            Ok(Llsd::Uuid(Uuid::from_slice(&buf)?))
152        }
153        b'd' => {
154            let mut buf = [0_u8; 8];
155            r.read_exact(&mut buf)?;
156            // Use little endian
157            let real = f64::from_le_bytes(buf);
158            let date = DateTime::<Utc>::from_timestamp(
159                real.trunc() as i64,
160                (real.fract() * 1_000_000_000.0) as u32,
161            );
162            Ok(Llsd::Date(date.unwrap_or_default()))
163        }
164        b'b' => {
165            let len = read_i32_be(r)? as usize;
166            let mut buf = vec![0; len];
167            r.read_exact(&mut buf)?;
168            Ok(Llsd::Binary(buf))
169        }
170        b'[' => {
171            let len = read_i32_be(r)? as usize;
172            let mut buf = Vec::with_capacity(len);
173            for _ in 0..len {
174                buf.push(from_reader_inner(r)?);
175            }
176            Ok(Llsd::Array(buf))
177        }
178        b'{' => {
179            let len = read_i32_be(r)? as usize;
180            let mut buf = std::collections::HashMap::with_capacity(len);
181            for _ in 0..len {
182                if read_u8(r)? != b'k' {
183                    return Err(anyhow::anyhow!("Expected 'k'"));
184                }
185                let key_len = read_i32_be(r)? as usize;
186                let mut key_buf = vec![0; key_len];
187                r.read_exact(&mut key_buf)?;
188                let key = String::from_utf8(key_buf)?;
189                let value = from_reader_inner(r)?;
190                buf.insert(key, value);
191            }
192            if read_u8(r)? != b'}' {
193                return Err(anyhow::anyhow!("Expected '}}'"));
194            }
195            Ok(Llsd::Map(buf))
196        }
197        b'"' => Ok(Llsd::String(unescape(r, b'"')?)),
198        b'\'' => Ok(Llsd::String(unescape(r, b'\'')?)),
199        other => Err(anyhow::anyhow!("Unknown LLSD type: {}", other)),
200    }
201}
202
203pub fn from_reader<R: Read>(r: &mut R) -> Result<Llsd, anyhow::Error> {
204    from_reader_inner(r)
205}
206
207pub fn from_slice(data: &[u8]) -> Result<Llsd, anyhow::Error> {
208    from_reader(&mut std::io::Cursor::new(data))
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use chrono::{TimeZone, Utc};
215    use std::collections::HashMap;
216
217    fn round_trip(llsd: Llsd) {
218        let encoded = to_vec(&llsd).expect("Failed to encode");
219        let decoded = from_slice(&encoded).expect("Failed to decode");
220        assert_eq!(llsd, decoded);
221    }
222
223    #[test]
224    fn undefined() {
225        round_trip(Llsd::Undefined);
226    }
227
228    #[test]
229    fn boolean() {
230        round_trip(Llsd::Boolean(true));
231        round_trip(Llsd::Boolean(false));
232    }
233
234    #[test]
235    fn integer() {
236        round_trip(Llsd::Integer(42));
237    }
238
239    #[test]
240    fn real() {
241        round_trip(Llsd::Real(13.1415));
242    }
243
244    #[test]
245    fn string() {
246        round_trip(Llsd::String("Hello, LLSD!".to_owned()));
247    }
248
249    #[test]
250    fn uri() {
251        round_trip(Llsd::Uri(Uri::parse("https://example.com/")));
252    }
253
254    #[test]
255    fn uuid() {
256        let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
257        round_trip(Llsd::Uuid(uuid));
258    }
259
260    #[test]
261    fn date() {
262        let dt = Utc.timestamp_opt(1_620_000_000, 0).unwrap();
263        round_trip(Llsd::Date(dt));
264    }
265
266    #[test]
267    fn binary() {
268        round_trip(Llsd::Binary(vec![0xde, 0xad, 0xbe, 0xef]));
269    }
270
271    #[test]
272    fn array() {
273        let arr = vec![
274            Llsd::Integer(1),
275            Llsd::String("two".into()),
276            Llsd::Boolean(false),
277        ];
278        round_trip(Llsd::Array(arr));
279    }
280
281    #[test]
282    fn map() {
283        let mut map = HashMap::new();
284        map.insert("answer".into(), Llsd::Integer(42));
285        map.insert("pi".into(), Llsd::Real(13.14));
286        map.insert("greeting".into(), Llsd::String("hello".into()));
287        round_trip(Llsd::Map(map));
288    }
289}