1pub const T_NOTHING: u8 = 0;
18pub const T_FALSE: u8 = 1;
20pub const T_TRUE: u8 = 2;
22pub const T_INT: u8 = 3;
24pub const T_FLOAT: u8 = 4;
26pub const T_TEXT: u8 = 6;
28pub const T_LIST: u8 = 13;
30pub const T_INDUCTIVE: u8 = 18;
32
33#[inline]
37pub fn write_uvarint(mut x: u64, out: &mut Vec<u8>) {
38 while x >= 0x80 {
39 out.push((x as u8) | 0x80);
40 x >>= 7;
41 }
42 out.push(x as u8);
43}
44
45#[inline]
47pub fn read_uvarint(buf: &[u8], pos: &mut usize) -> Option<u64> {
48 let mut result = 0u64;
49 let mut shift = 0u32;
50 loop {
51 let b = *buf.get(*pos)?;
52 *pos += 1;
53 if shift >= 64 {
54 return None;
55 }
56 result |= u64::from(b & 0x7f) << shift;
57 if b & 0x80 == 0 {
58 return Some(result);
59 }
60 shift += 7;
61 }
62}
63
64#[inline]
66pub fn zigzag(x: i64) -> u64 {
67 ((x << 1) ^ (x >> 63)) as u64
68}
69
70#[inline]
72pub fn unzigzag(x: u64) -> i64 {
73 ((x >> 1) as i64) ^ -((x & 1) as i64)
74}
75
76#[inline]
78pub fn write_str(s: &str, out: &mut Vec<u8>) {
79 write_uvarint(s.len() as u64, out);
80 out.extend_from_slice(s.as_bytes());
81}
82
83#[inline]
85pub fn read_str(buf: &[u8], pos: &mut usize) -> Option<String> {
86 let n = read_uvarint(buf, pos)? as usize;
87 let bytes = buf.get(*pos..pos.checked_add(n)?)?;
88 *pos += n;
89 String::from_utf8(bytes.to_vec()).ok()
90}
91
92#[inline]
94pub fn expect_tag(buf: &[u8], pos: &mut usize, expected: u8) -> Option<()> {
95 let t = *buf.get(*pos)?;
96 if t != expected {
97 return None;
98 }
99 *pos += 1;
100 Some(())
101}
102
103#[inline]
108pub fn write_inductive_header(out: &mut Vec<u8>, type_name: &str, constructor: &str, nargs: u64) {
109 out.push(T_INDUCTIVE);
110 write_str(type_name, out);
111 write_str(constructor, out);
112 write_uvarint(nargs, out);
113}
114
115#[inline]
118pub fn read_inductive_header(buf: &[u8], pos: &mut usize) -> Option<(String, String, u64)> {
119 expect_tag(buf, pos, T_INDUCTIVE)?;
120 let type_name = read_str(buf, pos)?;
121 let constructor = read_str(buf, pos)?;
122 let nargs = read_uvarint(buf, pos)?;
123 Some((type_name, constructor, nargs))
124}
125
126pub trait WireEncode {
130 fn wire_encode(&self, out: &mut Vec<u8>);
132}
133
134pub trait WireDecode: Sized {
136 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self>;
138}
139
140impl WireEncode for i64 {
141 fn wire_encode(&self, out: &mut Vec<u8>) {
142 out.push(T_INT);
143 write_uvarint(zigzag(*self), out);
144 }
145}
146impl WireDecode for i64 {
147 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
148 expect_tag(buf, pos, T_INT)?;
149 Some(unzigzag(read_uvarint(buf, pos)?))
150 }
151}
152
153impl WireEncode for bool {
154 fn wire_encode(&self, out: &mut Vec<u8>) {
155 out.push(if *self { T_TRUE } else { T_FALSE });
156 }
157}
158impl WireDecode for bool {
159 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
160 let t = *buf.get(*pos)?;
161 *pos += 1;
162 match t {
163 T_TRUE => Some(true),
164 T_FALSE => Some(false),
165 _ => None,
166 }
167 }
168}
169
170impl WireEncode for f64 {
171 fn wire_encode(&self, out: &mut Vec<u8>) {
172 out.push(T_FLOAT);
173 out.extend_from_slice(&self.to_le_bytes());
174 }
175}
176impl WireDecode for f64 {
177 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
178 expect_tag(buf, pos, T_FLOAT)?;
179 let b: [u8; 8] = buf.get(*pos..pos.checked_add(8)?)?.try_into().ok()?;
180 *pos += 8;
181 Some(f64::from_le_bytes(b))
182 }
183}
184
185impl WireEncode for String {
186 fn wire_encode(&self, out: &mut Vec<u8>) {
187 out.push(T_TEXT);
188 write_str(self, out);
189 }
190}
191impl WireDecode for String {
192 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
193 expect_tag(buf, pos, T_TEXT)?;
194 read_str(buf, pos)
195 }
196}
197
198impl<T: WireEncode + ?Sized> WireEncode for Box<T> {
199 fn wire_encode(&self, out: &mut Vec<u8>) {
200 (**self).wire_encode(out);
201 }
202}
203impl<T: WireDecode> WireDecode for Box<T> {
204 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
205 Some(Box::new(T::wire_decode(buf, pos)?))
206 }
207}
208
209impl<T: WireEncode> WireEncode for Vec<T> {
210 fn wire_encode(&self, out: &mut Vec<u8>) {
211 out.push(T_LIST);
212 write_uvarint(self.len() as u64, out);
213 for e in self {
214 e.wire_encode(out);
215 }
216 }
217}
218impl<T: WireDecode> WireDecode for Vec<T> {
219 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
220 expect_tag(buf, pos, T_LIST)?;
221 let n = read_uvarint(buf, pos)? as usize;
222 let mut xs = Vec::with_capacity(n.min(1024));
223 for _ in 0..n {
224 xs.push(T::wire_decode(buf, pos)?);
225 }
226 Some(xs)
227 }
228}
229
230impl<T: WireEncode> WireEncode for crate::types::LogosSeq<T> {
233 fn wire_encode(&self, out: &mut Vec<u8>) {
234 let inner = self.0.borrow();
235 out.push(T_LIST);
236 write_uvarint(inner.len() as u64, out);
237 for e in inner.iter() {
238 e.wire_encode(out);
239 }
240 }
241}
242impl<T: WireDecode> WireDecode for crate::types::LogosSeq<T> {
243 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
244 expect_tag(buf, pos, T_LIST)?;
245 let n = read_uvarint(buf, pos)? as usize;
246 let mut v = Vec::with_capacity(n.min(1024));
247 for _ in 0..n {
248 v.push(T::wire_decode(buf, pos)?);
249 }
250 Some(crate::types::LogosSeq::from_vec(v))
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[derive(Debug, Clone, PartialEq)]
262 enum Tree {
263 Leaf(i64),
264 Str(String),
265 Node { tag: String, flag: bool, ratio: f64, kids: Vec<Tree>, boxed: Box<Tree> },
266 Empty,
267 }
268
269 impl WireEncode for Tree {
270 fn wire_encode(&self, out: &mut Vec<u8>) {
271 match self {
272 Tree::Leaf(v) => {
273 write_inductive_header(out, "Tree", "Leaf", 1);
274 v.wire_encode(out);
275 }
276 Tree::Str(s) => {
277 write_inductive_header(out, "Tree", "Str", 1);
278 s.wire_encode(out);
279 }
280 Tree::Node { tag, flag, ratio, kids, boxed } => {
281 write_inductive_header(out, "Tree", "Node", 5);
282 tag.wire_encode(out);
283 flag.wire_encode(out);
284 ratio.wire_encode(out);
285 kids.wire_encode(out);
286 boxed.wire_encode(out);
287 }
288 Tree::Empty => write_inductive_header(out, "Tree", "Empty", 0),
289 }
290 }
291 }
292 impl WireDecode for Tree {
293 fn wire_decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
294 let (ty, ctor, _n) = read_inductive_header(buf, pos)?;
295 assert_eq!(ty, "Tree");
296 Some(match ctor.as_str() {
297 "Leaf" => Tree::Leaf(i64::wire_decode(buf, pos)?),
298 "Str" => Tree::Str(String::wire_decode(buf, pos)?),
299 "Node" => Tree::Node {
300 tag: String::wire_decode(buf, pos)?,
301 flag: bool::wire_decode(buf, pos)?,
302 ratio: f64::wire_decode(buf, pos)?,
303 kids: Vec::<Tree>::wire_decode(buf, pos)?,
304 boxed: Box::<Tree>::wire_decode(buf, pos)?,
305 },
306 "Empty" => Tree::Empty,
307 _ => return None,
308 })
309 }
310 }
311
312 fn roundtrip(t: &Tree) -> Tree {
313 let mut out = Vec::new();
314 t.wire_encode(&mut out);
315 let mut pos = 0usize;
316 let back = Tree::wire_decode(&out, &mut pos).expect("decode");
317 assert_eq!(pos, out.len(), "decoder must consume every byte");
318 back
319 }
320
321 #[test]
322 fn varint_roundtrips_across_the_range() {
323 for x in [0u64, 1, 127, 128, 16383, 16384, u32::MAX as u64, u64::MAX] {
324 let mut b = Vec::new();
325 write_uvarint(x, &mut b);
326 let mut p = 0;
327 assert_eq!(read_uvarint(&b, &mut p), Some(x));
328 assert_eq!(p, b.len());
329 }
330 }
331
332 #[test]
333 fn zigzag_roundtrips_including_extremes() {
334 for x in [0i64, 1, -1, 42, -42, i64::MAX, i64::MIN] {
335 assert_eq!(unzigzag(zigzag(x)), x);
336 }
337 }
338
339 #[test]
340 fn scalar_leaves_roundtrip() {
341 for v in [0i64, 1, -1, 42, -99999, i64::MAX, i64::MIN] {
342 assert_eq!(roundtrip(&Tree::Leaf(v)), Tree::Leaf(v));
343 }
344 }
345
346 #[test]
347 fn strings_roundtrip_including_empty_and_unicode() {
348 for s in ["", "x", "hello world", "héllo, 世界! + x_1", "\n\t\"quoted\""] {
349 assert_eq!(roundtrip(&Tree::Str(s.to_string())), Tree::Str(s.to_string()));
350 }
351 }
352
353 #[test]
354 fn nested_node_with_every_field_kind_roundtrips() {
355 let t = Tree::Node {
356 tag: "root".into(),
357 flag: true,
358 ratio: 3.5,
359 kids: vec![
360 Tree::Leaf(1),
361 Tree::Str("two".into()),
362 Tree::Node {
363 tag: "inner".into(),
364 flag: false,
365 ratio: -0.25,
366 kids: vec![],
367 boxed: Box::new(Tree::Empty),
368 },
369 ],
370 boxed: Box::new(Tree::Leaf(-7)),
371 };
372 assert_eq!(roundtrip(&t), t);
373 }
374
375 #[test]
376 fn empty_list_and_nullary_variant_roundtrip() {
377 assert_eq!(roundtrip(&Tree::Empty), Tree::Empty);
378 let t = Tree::Node { tag: "".into(), flag: false, ratio: 0.0, kids: vec![], boxed: Box::new(Tree::Empty) };
379 assert_eq!(roundtrip(&t), t);
380 }
381
382 #[test]
383 fn deep_recursion_roundtrips() {
384 let mut t = Tree::Leaf(0);
385 for i in 1..500 {
386 t = Tree::Node { tag: format!("n{i}"), flag: i % 2 == 0, ratio: i as f64, kids: vec![Tree::Leaf(i)], boxed: Box::new(t) };
387 }
388 assert_eq!(roundtrip(&t), t);
389 }
390
391 #[test]
392 fn logos_seq_roundtrips_and_matches_vec_bytes() {
393 use crate::types::LogosSeq;
394 let elems = vec![1i64, -2, 3, 0, i64::MAX];
395 let seq = LogosSeq::from_vec(elems.clone());
396
397 let mut seq_bytes = Vec::new();
398 seq.wire_encode(&mut seq_bytes);
399 let mut vec_bytes = Vec::new();
400 elems.wire_encode(&mut vec_bytes);
401 assert_eq!(seq_bytes, vec_bytes, "LogosSeq and Vec must share the T_LIST byte format");
402
403 let mut pos = 0usize;
404 let back = LogosSeq::<i64>::wire_decode(&seq_bytes, &mut pos).expect("decode");
405 assert_eq!(pos, seq_bytes.len());
406 assert_eq!(back.to_vec(), elems);
407
408 let empty = LogosSeq::<i64>::from_vec(vec![]);
410 let mut b = Vec::new();
411 empty.wire_encode(&mut b);
412 let mut p = 0usize;
413 assert_eq!(LogosSeq::<i64>::wire_decode(&b, &mut p).unwrap().to_vec(), Vec::<i64>::new());
414 }
415
416 #[test]
417 fn truncated_input_returns_none_never_panics() {
418 let mut out = Vec::new();
419 Tree::Leaf(123456).wire_encode(&mut out);
420 for cut in 0..out.len() {
421 let mut pos = 0usize;
422 let _ = Tree::wire_decode(&out[..cut], &mut pos); }
424 }
425}