Skip to main content

yog_data/
binary.rs

1//! The binary form — what the build compiles content to and the runtime and
2//! the Java hosts read, like NBT:
3//!
4//! ```text
5//! "YOGD" version:u8
6//! strings: varint count, then each as varint length + UTF-8
7//! root value
8//! ```
9//!
10//! A value is a tag byte and its payload; every string (map keys too) is an
11//! index into the table, so repeated keys and ids cost one varint each.
12//!
13//! | Tag | Value | Payload |
14//! |---:|---|---|
15//! | 0 | null | — |
16//! | 1, 2 | false, true | — |
17//! | 3 | int | zigzag varint |
18//! | 4 | float | f64, little-endian |
19//! | 5 | string | string |
20//! | 6 | list | varint count, values |
21//! | 7 | map | varint count, (key string, value) pairs |
22//! | 8 | ref | kind string, id string |
23//! | 9 | tag | kind string, id string |
24//! | 10 | resource | set string, key string |
25//! | 11 | text | string |
26//! | 12 | image | string |
27//! | 13 | color | u32, little-endian (`0xAARRGGBB`) |
28//! | 14 | translation key | string |
29//! | 15 | bytes | varint length, the bytes |
30//!
31//! Varints are LEB128. Decoding checks every length against the input, so
32//! malformed bytes are an error, never a panic or a huge allocation.
33
34use std::collections::HashMap;
35use std::fmt;
36use std::hash::{BuildHasherDefault, Hasher};
37
38use bumpalo::Bump;
39
40use crate::borrowed::{MapRef, ValueRef};
41use crate::value::{Map, Ref, Res, Value};
42
43pub const MAGIC: &[u8; 4] = b"YOGD";
44pub const VERSION: u8 = 1;
45
46/// Nested deeper than this is malformed.
47pub const MAX_DEPTH: usize = 256;
48
49mod tag {
50    pub const NULL: u8 = 0;
51    pub const FALSE: u8 = 1;
52    pub const TRUE: u8 = 2;
53    pub const INT: u8 = 3;
54    pub const FLOAT: u8 = 4;
55    pub const STR: u8 = 5;
56    pub const LIST: u8 = 6;
57    pub const MAP: u8 = 7;
58    pub const REF: u8 = 8;
59    pub const TAG: u8 = 9;
60    pub const RES: u8 = 10;
61    pub const TEXT: u8 = 11;
62    pub const IMAGE: u8 = 12;
63    pub const COLOR: u8 = 13;
64    pub const LANG: u8 = 14;
65    pub const BYTES: u8 = 15;
66}
67
68/// `value` in the binary form.
69pub fn encode(value: &Value) -> Vec<u8> {
70    let mut w = Writer::new();
71    w.value(value);
72    w.finish()
73}
74
75/// Writes the binary form piece by piece, from data that isn't a [`Value`]
76/// — nothing is built or copied on the way; strings are borrowed until
77/// [`Writer::finish`]. A map or list is given its length, then that many
78/// entries (`key` then a value) or values.
79///
80/// ```
81/// let mut w = yog_data::binary::Writer::new();
82/// w.map(2).key("id").str("mymod:ruby").key("count").int(3);
83/// let v = yog_data::binary::decode(&w.finish()).unwrap();
84/// assert_eq!(v.get("count").and_then(|c| c.as_i64()), Some(3));
85/// ```
86pub struct Writer<'v>(Encoder<'v>);
87
88impl Default for Writer<'_> {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl<'v> Writer<'v> {
95    pub fn new() -> Self {
96        Writer(Encoder { body: Vec::new(), strings: HashMap::default(), table: Vec::new() })
97    }
98
99    pub fn null(&mut self) -> &mut Self {
100        self.0.body.push(tag::NULL);
101        self
102    }
103
104    pub fn bool(&mut self, b: bool) -> &mut Self {
105        self.0.body.push(if b { tag::TRUE } else { tag::FALSE });
106        self
107    }
108
109    pub fn int(&mut self, n: i64) -> &mut Self {
110        self.0.body.push(tag::INT);
111        varint(&mut self.0.body, ((n << 1) ^ (n >> 63)) as u64);
112        self
113    }
114
115    pub fn float(&mut self, f: f64) -> &mut Self {
116        self.0.body.push(tag::FLOAT);
117        self.0.body.extend_from_slice(&f.to_le_bytes());
118        self
119    }
120
121    pub fn str(&mut self, s: &'v str) -> &mut Self {
122        self.0.body.push(tag::STR);
123        self.0.string(s);
124        self
125    }
126
127    /// A list of `len` values.
128    pub fn list(&mut self, len: usize) -> &mut Self {
129        self.0.body.push(tag::LIST);
130        varint(&mut self.0.body, len as u64);
131        self
132    }
133
134    /// A map of `len` entries, each a [`Writer::key`] and a value.
135    pub fn map(&mut self, len: usize) -> &mut Self {
136        self.0.body.push(tag::MAP);
137        varint(&mut self.0.body, len as u64);
138        self
139    }
140
141    pub fn key(&mut self, k: &'v str) -> &mut Self {
142        self.0.string(k);
143        self
144    }
145
146    pub fn value(&mut self, v: &'v Value) -> &mut Self {
147        self.0.value(v);
148        self
149    }
150
151    /// The bytes.
152    pub fn finish(self) -> Vec<u8> {
153        let enc = self.0;
154        let table_len: usize = enc.table.iter().map(|s| s.len() + 2).sum();
155        let mut out = Vec::with_capacity(5 + table_len + enc.body.len() + 4);
156        out.extend_from_slice(MAGIC);
157        out.push(VERSION);
158        varint(&mut out, enc.table.len() as u64);
159        for s in &enc.table {
160            varint(&mut out, s.len() as u64);
161            out.extend_from_slice(s.as_bytes());
162        }
163        out.extend_from_slice(&enc.body);
164        out
165    }
166}
167
168struct Encoder<'v> {
169    body: Vec<u8>,
170    strings: HashMap<&'v str, u32, BuildHasherDefault<Fx>>,
171    table: Vec<&'v str>,
172}
173
174impl<'v> Encoder<'v> {
175    fn string(&mut self, s: &'v str) {
176        let next = self.table.len() as u32;
177        let i = *self.strings.entry(s).or_insert_with(|| {
178            self.table.push(s);
179            next
180        });
181        varint(&mut self.body, i as u64);
182    }
183
184    fn value(&mut self, v: &'v Value) {
185        match v {
186            Value::Null => self.body.push(tag::NULL),
187            Value::Bool(false) => self.body.push(tag::FALSE),
188            Value::Bool(true) => self.body.push(tag::TRUE),
189            Value::Int(n) => {
190                self.body.push(tag::INT);
191                varint(&mut self.body, ((n << 1) ^ (n >> 63)) as u64);
192            }
193            Value::Float(f) => {
194                self.body.push(tag::FLOAT);
195                self.body.extend_from_slice(&f.to_le_bytes());
196            }
197            Value::Str(s) => {
198                self.body.push(tag::STR);
199                self.string(s);
200            }
201            Value::List(items) => {
202                self.body.push(tag::LIST);
203                varint(&mut self.body, items.len() as u64);
204                for item in items {
205                    self.value(item);
206                }
207            }
208            Value::Map(map) => {
209                self.body.push(tag::MAP);
210                varint(&mut self.body, map.len() as u64);
211                for (k, v) in map.iter() {
212                    self.string(k);
213                    self.value(v);
214                }
215            }
216            Value::Ref(r) | Value::Tag(r) => {
217                self.body.push(if matches!(v, Value::Ref(_)) {
218                    tag::REF
219                } else {
220                    tag::TAG
221                });
222                self.string(&r.kind);
223                self.string(&r.id);
224            }
225            Value::Res(r) => {
226                self.body.push(tag::RES);
227                self.string(&r.set);
228                self.string(&r.key);
229            }
230            Value::Text(s) => {
231                self.body.push(tag::TEXT);
232                self.string(s);
233            }
234            Value::Image(s) => {
235                self.body.push(tag::IMAGE);
236                self.string(s);
237            }
238            Value::Lang(s) => {
239                self.body.push(tag::LANG);
240                self.string(s);
241            }
242            Value::Color(c) => {
243                self.body.push(tag::COLOR);
244                self.body.extend_from_slice(&c.to_le_bytes());
245            }
246            Value::Bytes(b) => {
247                self.body.push(tag::BYTES);
248                varint(&mut self.body, b.len() as u64);
249                self.body.extend_from_slice(b);
250            }
251        }
252    }
253}
254
255/// rustc's Fx hash — the string table's keys are short and not adversarial.
256#[derive(Default)]
257struct Fx(u64);
258
259impl Fx {
260    fn add(&mut self, word: u64) {
261        self.0 = (self.0.rotate_left(5) ^ word).wrapping_mul(0x51_7c_c1_b7_27_22_0a_95);
262    }
263}
264
265impl Hasher for Fx {
266    fn write(&mut self, bytes: &[u8]) {
267        let (words, rest) = bytes.as_chunks::<8>();
268        for w in words {
269            self.add(u64::from_le_bytes(*w));
270        }
271        if !rest.is_empty() {
272            let mut b = [0u8; 8];
273            b[..rest.len()].copy_from_slice(rest);
274            self.add(u64::from_le_bytes(b));
275        }
276    }
277
278    fn write_u8(&mut self, b: u8) {
279        self.add(b as u64);
280    }
281
282    fn finish(&self) -> u64 {
283        self.0
284    }
285}
286
287fn varint(out: &mut Vec<u8>, mut n: u64) {
288    while n >= 0x80 {
289        out.push((n as u8) | 0x80);
290        n >>= 7;
291    }
292    out.push(n as u8);
293}
294
295/// Malformed binary data.
296#[derive(Debug, Clone, PartialEq, Eq)]
297pub struct DecodeError {
298    pub message: String,
299    pub offset: usize,
300}
301
302impl fmt::Display for DecodeError {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        write!(f, "byte {}: {}", self.offset, self.message)
305    }
306}
307
308impl std::error::Error for DecodeError {}
309
310/// Reads the binary form.
311pub fn decode(bytes: &[u8]) -> Result<Value, DecodeError> {
312    let mut d = Decoder::open(bytes)?;
313    let v = d.value(0)?;
314    d.end()?;
315    Ok(v)
316}
317
318/// Reads the binary form borrowed: every string points into `bytes`,
319/// lists and maps go in `arena` — nothing is copied. What a schema reads
320/// fastest ([`crate::read_bytes`]).
321pub fn decode_ref<'a>(bytes: &'a [u8], arena: &'a Bump) -> Result<ValueRef<'a>, DecodeError> {
322    let mut d = Decoder::open(bytes)?;
323    let mut stack = Vec::new();
324    let v = d.value_ref(0, arena, &mut stack)?;
325    d.end()?;
326    Ok(v)
327}
328
329impl<'b> Decoder<'b> {
330    /// The header and the string table read; the root value next.
331    fn open(bytes: &'b [u8]) -> Result<Self, DecodeError> {
332        let mut d = Decoder {
333            b: bytes,
334            i: 0,
335            strings: Vec::new(),
336            keys: Vec::new(),
337        };
338        if !bytes.starts_with(MAGIC) {
339            return Err(d.error("not Yog Data (no `YOGD` header)"));
340        }
341        d.i = 4;
342        let version = d.byte()?;
343        if version != VERSION {
344            return Err(d.error(format!(
345                "Yog Data version {version}, this reads version {VERSION}"
346            )));
347        }
348        let count = d.count(1)?;
349        d.strings.reserve(count);
350        for _ in 0..count {
351            let len = d.count(1)?;
352            let start = d.i;
353            let s = std::str::from_utf8(&bytes[start..start + len]).map_err(|_| DecodeError {
354                message: "a string isn't UTF-8".into(),
355                offset: start,
356            })?;
357            d.strings.push(s);
358            d.i += len;
359        }
360        Ok(d)
361    }
362
363    fn end(&self) -> Result<(), DecodeError> {
364        if self.i != self.b.len() {
365            return Err(self.error("bytes after the value"));
366        }
367        Ok(())
368    }
369}
370
371struct Decoder<'b> {
372    b: &'b [u8],
373    i: usize,
374    strings: Vec<&'b str>,
375    /// Map keys made so far, by table index: each key is allocated once.
376    keys: Vec<Option<std::sync::Arc<str>>>,
377}
378
379impl<'b> Decoder<'b> {
380    fn error(&self, message: impl Into<String>) -> DecodeError {
381        DecodeError {
382            message: message.into(),
383            offset: self.i,
384        }
385    }
386
387    fn byte(&mut self) -> Result<u8, DecodeError> {
388        let b = *self
389            .b
390            .get(self.i)
391            .ok_or_else(|| self.error("unexpected end"))?;
392        self.i += 1;
393        Ok(b)
394    }
395
396    fn varint(&mut self) -> Result<u64, DecodeError> {
397        let mut n = 0u64;
398        for shift in (0..64).step_by(7) {
399            let b = self.byte()?;
400            n |= ((b & 0x7F) as u64) << shift;
401            if b & 0x80 == 0 {
402                return Ok(n);
403            }
404        }
405        Err(self.error("varint longer than 64 bits"))
406    }
407
408    /// A length, checked against what's left: each of the `count` things takes at least `min` bytes.
409    fn count(&mut self, min: usize) -> Result<usize, DecodeError> {
410        let n = self.varint()?;
411        let left = self.b.len() - self.i;
412        if n > (left / min.max(1)) as u64 {
413            return Err(self.error(format!("length {n} past the end ({left} bytes left)")));
414        }
415        Ok(n as usize)
416    }
417
418    fn fixed<const N: usize>(&mut self) -> Result<[u8; N], DecodeError> {
419        let bytes = self
420            .b
421            .get(self.i..self.i + N)
422            .ok_or_else(|| self.error("unexpected end"))?;
423        self.i += N;
424        Ok(bytes.try_into().unwrap())
425    }
426
427    /// A length and that many bytes.
428    fn bytes(&mut self) -> Result<&'b [u8], DecodeError> {
429        let len = usize::try_from(self.varint()?).map_err(|_| self.error("a length beyond memory"))?;
430        let end = self.i.checked_add(len).filter(|&e| e <= self.b.len()).ok_or_else(|| self.error("bytes past the end"))?;
431        let out = &self.b[self.i..end];
432        self.i = end;
433        Ok(out)
434    }
435
436    fn string(&mut self) -> Result<&'b str, DecodeError> {
437        let i = self.varint()?;
438        self.strings
439            .get(i as usize)
440            .copied()
441            .ok_or_else(|| self.error(format!("string {i} isn't in the table")))
442    }
443
444    /// A map key: shared with every other map that has it.
445    fn key(&mut self) -> Result<std::sync::Arc<str>, DecodeError> {
446        let i = self.varint()? as usize;
447        if let Some(Some(k)) = self.keys.get(i) {
448            return Ok(k.clone());
449        }
450        let s = *self.strings.get(i).ok_or_else(|| self.error(format!("string {i} isn't in the table")))?;
451        let k: std::sync::Arc<str> = s.into();
452        if self.keys.len() <= i {
453            self.keys.resize(self.strings.len(), None);
454        }
455        self.keys[i] = Some(k.clone());
456        Ok(k)
457    }
458
459    /// [`Decoder::value`], borrowed: children gather on `stack`, then move
460    /// into `arena` as one slice.
461    fn value_ref<'a>(&mut self, depth: usize, arena: &'a Bump, stack: &mut Vec<ValueRef<'a>>) -> Result<ValueRef<'a>, DecodeError>
462    where
463        'b: 'a,
464    {
465        if depth > MAX_DEPTH {
466            return Err(self.error(format!("nested deeper than {MAX_DEPTH} levels")));
467        }
468        Ok(match self.byte()? {
469            tag::NULL => ValueRef::Null,
470            tag::FALSE => ValueRef::Bool(false),
471            tag::TRUE => ValueRef::Bool(true),
472            tag::INT => {
473                let z = self.varint()?;
474                ValueRef::Int(((z >> 1) as i64) ^ -((z & 1) as i64))
475            }
476            tag::FLOAT => ValueRef::Float(f64::from_le_bytes(self.fixed()?)),
477            tag::STR => ValueRef::Str(self.string()?),
478            tag::LIST => {
479                let n = self.count(1)?;
480                let start = stack.len();
481                for _ in 0..n {
482                    let v = self.value_ref(depth + 1, arena, stack)?;
483                    stack.push(v);
484                }
485                let items = arena.alloc_slice_copy(&stack[start..]);
486                stack.truncate(start);
487                ValueRef::List(items)
488            }
489            tag::MAP => {
490                let n = self.count(2)?;
491                let mut entries = bumpalo::collections::Vec::with_capacity_in(n, arena);
492                for _ in 0..n {
493                    let k = self.string()?;
494                    entries.push((k, self.value_ref(depth + 1, arena, stack)?));
495                }
496                ValueRef::Map(MapRef(entries.into_bump_slice()))
497            }
498            t @ (tag::REF | tag::TAG) => {
499                let (kind, id) = (self.string()?, self.string()?);
500                if t == tag::REF { ValueRef::Ref { kind, id } } else { ValueRef::Tag { kind, id } }
501            }
502            tag::RES => ValueRef::Res { set: self.string()?, key: self.string()? },
503            tag::TEXT => ValueRef::Text(self.string()?),
504            tag::IMAGE => ValueRef::Image(self.string()?),
505            tag::COLOR => ValueRef::Color(u32::from_le_bytes(self.fixed()?)),
506            tag::LANG => ValueRef::Lang(self.string()?),
507            tag::BYTES => ValueRef::Bytes(self.bytes()?),
508            t => {
509                self.i -= 1;
510                return Err(self.error(format!("unknown tag {t}")));
511            }
512        })
513    }
514
515    fn value(&mut self, depth: usize) -> Result<Value, DecodeError> {
516        if depth > MAX_DEPTH {
517            return Err(self.error(format!("nested deeper than {MAX_DEPTH} levels")));
518        }
519        Ok(match self.byte()? {
520            tag::NULL => Value::Null,
521            tag::FALSE => Value::Bool(false),
522            tag::TRUE => Value::Bool(true),
523            tag::INT => {
524                let z = self.varint()?;
525                Value::Int(((z >> 1) as i64) ^ -((z & 1) as i64))
526            }
527            tag::FLOAT => Value::Float(f64::from_le_bytes(self.fixed()?)),
528            tag::STR => Value::Str(self.string()?.to_string()),
529            tag::LIST => {
530                let n = self.count(1)?;
531                let mut items = Vec::with_capacity(n);
532                for _ in 0..n {
533                    items.push(self.value(depth + 1)?);
534                }
535                Value::List(items)
536            }
537            tag::MAP => {
538                let n = self.count(2)?;
539                let mut map = Map::with_capacity(n);
540                for _ in 0..n {
541                    let k = self.key()?;
542                    map.push_unchecked(k, self.value(depth + 1)?);
543                }
544                Value::Map(map)
545            }
546            t @ (tag::REF | tag::TAG) => {
547                let r = Ref {
548                    kind: self.string()?.to_string(),
549                    id: self.string()?.to_string(),
550                };
551                if t == tag::REF {
552                    Value::Ref(r)
553                } else {
554                    Value::Tag(r)
555                }
556            }
557            tag::RES => Value::Res(Res {
558                set: self.string()?.to_string(),
559                key: self.string()?.to_string(),
560            }),
561            tag::TEXT => Value::Text(self.string()?.to_string()),
562            tag::IMAGE => Value::Image(self.string()?.to_string()),
563            tag::COLOR => Value::Color(u32::from_le_bytes(self.fixed()?)),
564            tag::LANG => Value::Lang(self.string()?.to_string()),
565            tag::BYTES => Value::Bytes(self.bytes()?.to_vec()),
566            t => {
567                self.i -= 1;
568                return Err(self.error(format!("unknown tag {t}")));
569            }
570        })
571    }
572}
573
574#[cfg(test)]
575mod tests {
576    use super::*;
577
578    fn sample() -> Value {
579        let mut m = Map::new();
580        m.insert("name", Value::Str("Ruby".into()));
581        m.insert(
582            "n",
583            Value::List(vec![
584                Value::Int(0),
585                Value::Int(-1),
586                Value::Int(i64::MAX),
587                Value::Int(i64::MIN),
588                Value::Float(0.5),
589                Value::Null,
590                Value::Bool(true),
591                Value::Bool(false),
592            ]),
593        );
594        m.insert("item", Value::Ref(Ref::new("item", "minecraft:stick")));
595        m.insert("tag", Value::Tag(Ref::new("item", "minecraft:planks")));
596        m.insert(
597            "title",
598            Value::Res(Res {
599                set: "Strings".into(),
600                key: "title".into(),
601            }),
602        );
603        m.insert("text", Value::Text("{b|hi}".into()));
604        m.insert("icon", Value::Image("sprite:x".into()));
605        m.insert("color", Value::Color(0x80FF0000));
606        m.insert("lang", Value::Lang("gui.done".into()));
607        m.insert("again", Value::Str("Ruby".into()));
608        Value::Map(m)
609    }
610
611    #[test]
612    fn bytes_round_trip_in_both_forms() {
613        let v = Value::Bytes(vec![0, 1, 2, 255]);
614        assert_eq!(decode(&encode(&v)).unwrap(), v);
615        let text = crate::text::to_string(&v);
616        assert_eq!(text, "\"@bytes:AAEC/w==\"");
617        assert_eq!(crate::typed::classify("@bytes:AAEC/w==").unwrap(), v);
618        assert!(decode(&[b'Y', b'O', b'G', b'D', VERSION, 0, tag::BYTES, 9]).is_err(), "a length past the end");
619    }
620
621    #[test]
622    fn round_trips_every_type() {
623        let v = sample();
624        let bytes = encode(&v);
625        assert_eq!(decode(&bytes).unwrap(), v);
626        // "Ruby" is stored once.
627        assert_eq!(bytes.windows(4).filter(|w| w == b"Ruby").count(), 1);
628    }
629
630    #[test]
631    fn the_borrowed_decode_reads_the_same_without_copying() {
632        let v = sample();
633        let bytes = encode(&v);
634        let arena = Bump::new();
635        let r = decode_ref(&bytes, &arena).unwrap();
636        assert_eq!(r.to_value(), v);
637        assert_eq!(r, v.borrow(&arena), "borrowing a Value gives the same");
638        // Its strings point into the bytes.
639        let span = bytes.as_ptr_range();
640        let mut strings = Vec::new();
641        fn walk<'a>(v: &ValueRef<'a>, out: &mut Vec<&'a str>) {
642            match v {
643                ValueRef::Str(s) | ValueRef::Text(s) | ValueRef::Image(s) | ValueRef::Lang(s) => out.push(s),
644                ValueRef::List(l) => l.iter().for_each(|x| walk(x, out)),
645                ValueRef::Map(m) => m.iter().for_each(|(k, x)| {
646                    out.push(k);
647                    walk(x, out)
648                }),
649                _ => {}
650            }
651        }
652        walk(&r, &mut strings);
653        assert!(!strings.is_empty() && strings.iter().all(|s| span.contains(&s.as_ptr())));
654        // Malformed bytes are errors here too.
655        for len in 0..bytes.len() {
656            assert!(decode_ref(&bytes[..len], &arena).is_err(), "truncated at {len}");
657        }
658    }
659
660    #[test]
661    fn malformed_bytes_are_errors() {
662        let bytes = encode(&sample());
663        // Every truncation fails cleanly.
664        for len in 0..bytes.len() {
665            assert!(decode(&bytes[..len]).is_err(), "truncated at {len}");
666        }
667        let mut extra = bytes.clone();
668        extra.push(0);
669        assert!(
670            decode(&extra)
671                .unwrap_err()
672                .message
673                .contains("after the value")
674        );
675        assert!(
676            decode(b"YOGD\x02\x00\x00")
677                .unwrap_err()
678                .message
679                .contains("version 2")
680        );
681        assert!(
682            decode(b"YOGD\x01\x00\x63")
683                .unwrap_err()
684                .message
685                .contains("unknown tag")
686        );
687        assert!(
688            decode(b"YOGD\x01\x00\x05\x00")
689                .unwrap_err()
690                .message
691                .contains("isn't in the table")
692        );
693        // A huge count with nothing behind it doesn't allocate.
694        assert!(
695            decode(b"YOGD\x01\x00\x06\xff\xff\xff\xff\x0f")
696                .unwrap_err()
697                .message
698                .contains("past the end")
699        );
700        let deep: Vec<u8> = b"YOGD\x01\x00"
701            .iter()
702            .copied()
703            .chain(std::iter::repeat_n([6u8, 1], 300).flatten())
704            .chain([0])
705            .collect();
706        assert!(decode(&deep).unwrap_err().message.contains("nested deeper"));
707    }
708}