Skip to main content

yog_data/binary/
mod.rs

1//! The binary form — what the build compiles content to, what the bus
2//! carries, and what the runtime and the Java hosts read, like NBT:
3//!
4//! ```text
5//! "YOGD" version:u8
6//! root value
7//! ```
8//!
9//! A value is a tag byte and its payload. A string is written where it's
10//! first used and referred to after that: a varint `v`, then — when `v` is
11//! odd — `v >> 1` bytes of UTF-8, the next entry of the string table; when
12//! it's even, the table's entry `v >> 1`. Repeated keys and ids cost one
13//! varint each, and the writer never goes back over what it wrote.
14//!
15//! | Tag | Value | Payload |
16//! |---:|---|---|
17//! | 0 | null | — |
18//! | 1, 2 | false, true | — |
19//! | 3 | int | zigzag varint |
20//! | 4 | float | f64, little-endian |
21//! | 5 | string | string |
22//! | 6 | list | varint count, values |
23//! | 7 | map | varint count, (key string, value) pairs |
24//! | 8 | ref | kind string, id string |
25//! | 9 | tag | kind string, id string |
26//! | 10 | resource | set string, key string |
27//! | 11 | text | string |
28//! | 12 | image | string |
29//! | 13 | color | u32, little-endian (`0xAARRGGBB`) |
30//! | 14 | translation key | string |
31//! | 15 | bytes | varint length, the bytes |
32//! | 16 | list of ints, packed | varint count, zigzag varints |
33//! | 17 | stream | u32 byte length, then varint count and values with their own strings |
34//! | 18 | float, 32-bit | f32, little-endian |
35//! | 19 | list of floats, packed | varint count, f64 each, little-endian |
36//! | 20 | list of 32-bit floats, packed | varint count, f32 each, little-endian |
37//!
38//! Nothing is aligned: numbers are read out of the bytes wherever they are,
39//! so no padding is ever written.
40//!
41//! Varints are LEB128. Decoding checks every length against the input, so
42//! malformed bytes are an error, never a panic or a huge allocation.
43//!
44//! A [`crate::Schema`] writes itself here straight ([`crate::Schema::encode`])
45//! and reads itself back the same way ([`crate::Schema::decode`]) — no value
46//! tree on the way.
47
48mod decoder;
49pub(crate) mod interner;
50#[cfg(test)]
51mod tests;
52pub(crate) mod writer;
53
54use std::fmt;
55
56use bumpalo::Bump;
57
58pub use decoder::Decoder;
59pub use writer::Writer;
60
61use crate::borrowed::ValueRef;
62use crate::value::Value;
63
64pub const MAGIC: &[u8; 4] = b"YOGD";
65pub const VERSION: u8 = 2;
66
67/// Nested deeper than this is malformed.
68pub const MAX_DEPTH: usize = 256;
69
70/// A value's first byte: what it is.
71pub mod tag {
72    /// The tags, each a constant, and [`ALL`] of them by name.
73    macro_rules! tags {
74        ($($(#[$doc:meta])* $name:ident = $value:expr;)*) => {
75            $($(#[$doc])* pub const $name: u8 = $value;)*
76            /// Every tag, by its name (what other languages' readers are generated from).
77            pub const ALL: &[(&str, u8)] = &[$((stringify!($name), $value)),*];
78        };
79    }
80
81    tags! {
82        NULL = 0;
83        FALSE = 1;
84        TRUE = 2;
85        INT = 3;
86        FLOAT = 4;
87        STR = 5;
88        LIST = 6;
89        MAP = 7;
90        REF = 8;
91        TAG = 9;
92        RES = 10;
93        TEXT = 11;
94        IMAGE = 12;
95        COLOR = 13;
96        LANG = 14;
97        BYTES = 15;
98        /// A list of ints without a tag each: what a schema of numbers writes its lists as.
99        INTS = 16;
100        /// A list in a body of its own (its own strings): read lazily, cut out whole ([`crate::Stream`]).
101        STREAM = 17;
102        FLOAT32 = 18;
103        /// Lists of floats without a tag each.
104        FLOATS = 19;
105        FLOATS32 = 20;
106    }
107}
108
109/// A stream's length: fixed width, so the writer fills it in after its items.
110pub(crate) const STREAM_LEN_BYTES: usize = 4;
111
112/// A string's varint: a new one's (odd, its length) or a reference to the
113/// table (even, its index).
114pub(crate) const NEW_STRING: u64 = 1;
115
116/// The low seven bits of a varint byte, and the bit saying more follow.
117pub(crate) const VARINT_BITS: u64 = 0x7F;
118pub(crate) const VARINT_MORE: u8 = 0x80;
119pub(crate) const VARINT_SHIFT: u32 = 7;
120
121pub(crate) fn varint(out: &mut Vec<u8>, mut n: u64) {
122    while n > VARINT_BITS {
123        out.push((n as u8) | VARINT_MORE);
124        n >>= VARINT_SHIFT;
125    }
126    out.push(n as u8);
127}
128
129/// The bits a varint can hold.
130pub(crate) const VARINT_MAX_SHIFT: u32 = 64;
131
132/// The varint at `*i` in `b`, `*i` moved past it; `None` cut short or longer than 64 bits.
133pub(crate) fn read_varint(b: &[u8], i: &mut usize) -> Option<u64> {
134    let mut n = 0u64;
135    let mut shift = 0;
136    while shift < VARINT_MAX_SHIFT {
137        let byte = *b.get(*i)?;
138        *i += 1;
139        n |= (u64::from(byte) & VARINT_BITS) << shift;
140        if byte & VARINT_MORE == 0 {
141            return Some(n);
142        }
143        shift += VARINT_SHIFT;
144    }
145    None
146}
147
148/// `n` zigzagged: small negative numbers stay small varints.
149pub(crate) fn zigzag(n: i64) -> u64 {
150    ((n << 1) ^ (n >> 63)) as u64
151}
152
153pub(crate) fn unzigzag(z: u64) -> i64 {
154    ((z >> 1) as i64) ^ -((z & 1) as i64)
155}
156
157/// Malformed binary data.
158#[derive(Debug, Clone, PartialEq, Eq)]
159pub struct DecodeError {
160    pub message: String,
161    pub offset: usize,
162}
163
164impl fmt::Display for DecodeError {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        write!(f, "byte {}: {}", self.offset, self.message)
167    }
168}
169
170impl std::error::Error for DecodeError {}
171
172/// `value` in the binary form.
173pub fn encode(value: &Value) -> Vec<u8> {
174    let mut w = Writer::new();
175    w.value(value);
176    w.finish()
177}
178
179/// Reads the binary form.
180pub fn decode(bytes: &[u8]) -> Result<Value, DecodeError> {
181    let mut d = Decoder::open(bytes)?;
182    let v = d.value()?;
183    d.end()?;
184    Ok(v)
185}
186
187/// Reads the binary form borrowed: every string points into `bytes`,
188/// lists and maps go in `arena` — nothing is copied.
189pub fn decode_ref<'a>(bytes: &'a [u8], arena: &'a Bump) -> Result<ValueRef<'a>, DecodeError> {
190    let mut d = Decoder::open(bytes)?;
191    let v = d.value_ref(arena)?;
192    d.end()?;
193    Ok(v)
194}