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}