1use std::collections::HashMap;
33use std::fmt;
34use std::hash::{BuildHasherDefault, Hasher};
35
36use crate::value::{Map, Ref, Res, Value};
37
38pub const MAGIC: &[u8; 4] = b"YOGD";
39pub const VERSION: u8 = 1;
40
41pub const MAX_DEPTH: usize = 256;
43
44mod tag {
45 pub const NULL: u8 = 0;
46 pub const FALSE: u8 = 1;
47 pub const TRUE: u8 = 2;
48 pub const INT: u8 = 3;
49 pub const FLOAT: u8 = 4;
50 pub const STR: u8 = 5;
51 pub const LIST: u8 = 6;
52 pub const MAP: u8 = 7;
53 pub const REF: u8 = 8;
54 pub const TAG: u8 = 9;
55 pub const RES: u8 = 10;
56 pub const TEXT: u8 = 11;
57 pub const IMAGE: u8 = 12;
58 pub const COLOR: u8 = 13;
59}
60
61pub fn encode(value: &Value) -> Vec<u8> {
63 let mut enc = Encoder {
64 body: Vec::new(),
65 strings: HashMap::default(),
66 table: Vec::new(),
67 };
68 enc.value(value);
69 let table_len: usize = enc.table.iter().map(|s| s.len() + 2).sum();
70 let mut out = Vec::with_capacity(5 + table_len + enc.body.len() + 4);
71 out.extend_from_slice(MAGIC);
72 out.push(VERSION);
73 varint(&mut out, enc.table.len() as u64);
74 for s in &enc.table {
75 varint(&mut out, s.len() as u64);
76 out.extend_from_slice(s.as_bytes());
77 }
78 out.extend_from_slice(&enc.body);
79 out
80}
81
82struct Encoder<'v> {
83 body: Vec<u8>,
84 strings: HashMap<&'v str, u32, BuildHasherDefault<Fx>>,
85 table: Vec<&'v str>,
86}
87
88impl<'v> Encoder<'v> {
89 fn string(&mut self, s: &'v str) {
90 let next = self.table.len() as u32;
91 let i = *self.strings.entry(s).or_insert_with(|| {
92 self.table.push(s);
93 next
94 });
95 varint(&mut self.body, i as u64);
96 }
97
98 fn value(&mut self, v: &'v Value) {
99 match v {
100 Value::Null => self.body.push(tag::NULL),
101 Value::Bool(false) => self.body.push(tag::FALSE),
102 Value::Bool(true) => self.body.push(tag::TRUE),
103 Value::Int(n) => {
104 self.body.push(tag::INT);
105 varint(&mut self.body, ((n << 1) ^ (n >> 63)) as u64);
106 }
107 Value::Float(f) => {
108 self.body.push(tag::FLOAT);
109 self.body.extend_from_slice(&f.to_le_bytes());
110 }
111 Value::Str(s) => {
112 self.body.push(tag::STR);
113 self.string(s);
114 }
115 Value::List(items) => {
116 self.body.push(tag::LIST);
117 varint(&mut self.body, items.len() as u64);
118 for item in items {
119 self.value(item);
120 }
121 }
122 Value::Map(map) => {
123 self.body.push(tag::MAP);
124 varint(&mut self.body, map.len() as u64);
125 for (k, v) in map.iter() {
126 self.string(k);
127 self.value(v);
128 }
129 }
130 Value::Ref(r) | Value::Tag(r) => {
131 self.body.push(if matches!(v, Value::Ref(_)) {
132 tag::REF
133 } else {
134 tag::TAG
135 });
136 self.string(&r.kind);
137 self.string(&r.id);
138 }
139 Value::Res(r) => {
140 self.body.push(tag::RES);
141 self.string(&r.set);
142 self.string(&r.key);
143 }
144 Value::Text(s) => {
145 self.body.push(tag::TEXT);
146 self.string(s);
147 }
148 Value::Image(s) => {
149 self.body.push(tag::IMAGE);
150 self.string(s);
151 }
152 Value::Color(c) => {
153 self.body.push(tag::COLOR);
154 self.body.extend_from_slice(&c.to_le_bytes());
155 }
156 }
157 }
158}
159
160#[derive(Default)]
162struct Fx(u64);
163
164impl Fx {
165 fn add(&mut self, word: u64) {
166 self.0 = (self.0.rotate_left(5) ^ word).wrapping_mul(0x51_7c_c1_b7_27_22_0a_95);
167 }
168}
169
170impl Hasher for Fx {
171 fn write(&mut self, bytes: &[u8]) {
172 let (words, rest) = bytes.as_chunks::<8>();
173 for w in words {
174 self.add(u64::from_le_bytes(*w));
175 }
176 if !rest.is_empty() {
177 let mut b = [0u8; 8];
178 b[..rest.len()].copy_from_slice(rest);
179 self.add(u64::from_le_bytes(b));
180 }
181 }
182
183 fn write_u8(&mut self, b: u8) {
184 self.add(b as u64);
185 }
186
187 fn finish(&self) -> u64 {
188 self.0
189 }
190}
191
192fn varint(out: &mut Vec<u8>, mut n: u64) {
193 while n >= 0x80 {
194 out.push((n as u8) | 0x80);
195 n >>= 7;
196 }
197 out.push(n as u8);
198}
199
200#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct DecodeError {
203 pub message: String,
204 pub offset: usize,
205}
206
207impl fmt::Display for DecodeError {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(f, "byte {}: {}", self.offset, self.message)
210 }
211}
212
213impl std::error::Error for DecodeError {}
214
215pub fn decode(bytes: &[u8]) -> Result<Value, DecodeError> {
217 let mut d = Decoder {
218 b: bytes,
219 i: 0,
220 strings: Vec::new(),
221 };
222 if !bytes.starts_with(MAGIC) {
223 return Err(d.error("not Yog Data (no `YOGD` header)"));
224 }
225 d.i = 4;
226 let version = d.byte()?;
227 if version != VERSION {
228 return Err(d.error(format!(
229 "Yog Data version {version}, this reads version {VERSION}"
230 )));
231 }
232 let count = d.count(1)?;
233 d.strings.reserve(count);
234 for _ in 0..count {
235 let len = d.count(1)?;
236 let start = d.i;
237 let s = std::str::from_utf8(&bytes[start..start + len]).map_err(|_| DecodeError {
238 message: "a string isn't UTF-8".into(),
239 offset: start,
240 })?;
241 d.strings.push(s);
242 d.i += len;
243 }
244 let v = d.value(0)?;
245 if d.i != bytes.len() {
246 return Err(d.error("bytes after the value"));
247 }
248 Ok(v)
249}
250
251struct Decoder<'b> {
252 b: &'b [u8],
253 i: usize,
254 strings: Vec<&'b str>,
255}
256
257impl<'b> Decoder<'b> {
258 fn error(&self, message: impl Into<String>) -> DecodeError {
259 DecodeError {
260 message: message.into(),
261 offset: self.i,
262 }
263 }
264
265 fn byte(&mut self) -> Result<u8, DecodeError> {
266 let b = *self
267 .b
268 .get(self.i)
269 .ok_or_else(|| self.error("unexpected end"))?;
270 self.i += 1;
271 Ok(b)
272 }
273
274 fn varint(&mut self) -> Result<u64, DecodeError> {
275 let mut n = 0u64;
276 for shift in (0..64).step_by(7) {
277 let b = self.byte()?;
278 n |= ((b & 0x7F) as u64) << shift;
279 if b & 0x80 == 0 {
280 return Ok(n);
281 }
282 }
283 Err(self.error("varint longer than 64 bits"))
284 }
285
286 fn count(&mut self, min: usize) -> Result<usize, DecodeError> {
288 let n = self.varint()?;
289 let left = self.b.len() - self.i;
290 if n > (left / min.max(1)) as u64 {
291 return Err(self.error(format!("length {n} past the end ({left} bytes left)")));
292 }
293 Ok(n as usize)
294 }
295
296 fn fixed<const N: usize>(&mut self) -> Result<[u8; N], DecodeError> {
297 let bytes = self
298 .b
299 .get(self.i..self.i + N)
300 .ok_or_else(|| self.error("unexpected end"))?;
301 self.i += N;
302 Ok(bytes.try_into().unwrap())
303 }
304
305 fn string(&mut self) -> Result<&'b str, DecodeError> {
306 let i = self.varint()?;
307 self.strings
308 .get(i as usize)
309 .copied()
310 .ok_or_else(|| self.error(format!("string {i} isn't in the table")))
311 }
312
313 fn value(&mut self, depth: usize) -> Result<Value, DecodeError> {
314 if depth > MAX_DEPTH {
315 return Err(self.error(format!("nested deeper than {MAX_DEPTH} levels")));
316 }
317 Ok(match self.byte()? {
318 tag::NULL => Value::Null,
319 tag::FALSE => Value::Bool(false),
320 tag::TRUE => Value::Bool(true),
321 tag::INT => {
322 let z = self.varint()?;
323 Value::Int(((z >> 1) as i64) ^ -((z & 1) as i64))
324 }
325 tag::FLOAT => Value::Float(f64::from_le_bytes(self.fixed()?)),
326 tag::STR => Value::Str(self.string()?.to_string()),
327 tag::LIST => {
328 let n = self.count(1)?;
329 let mut items = Vec::with_capacity(n);
330 for _ in 0..n {
331 items.push(self.value(depth + 1)?);
332 }
333 Value::List(items)
334 }
335 tag::MAP => {
336 let n = self.count(2)?;
337 let mut map = Map::with_capacity(n);
338 for _ in 0..n {
339 let k = self.string()?.to_string();
340 map.push_unchecked(k, self.value(depth + 1)?);
341 }
342 Value::Map(map)
343 }
344 t @ (tag::REF | tag::TAG) => {
345 let r = Ref {
346 kind: self.string()?.to_string(),
347 id: self.string()?.to_string(),
348 };
349 if t == tag::REF {
350 Value::Ref(r)
351 } else {
352 Value::Tag(r)
353 }
354 }
355 tag::RES => Value::Res(Res {
356 set: self.string()?.to_string(),
357 key: self.string()?.to_string(),
358 }),
359 tag::TEXT => Value::Text(self.string()?.to_string()),
360 tag::IMAGE => Value::Image(self.string()?.to_string()),
361 tag::COLOR => Value::Color(u32::from_le_bytes(self.fixed()?)),
362 t => {
363 self.i -= 1;
364 return Err(self.error(format!("unknown tag {t}")));
365 }
366 })
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 fn sample() -> Value {
375 let mut m = Map::new();
376 m.insert("name", Value::Str("Ruby".into()));
377 m.insert(
378 "n",
379 Value::List(vec![
380 Value::Int(0),
381 Value::Int(-1),
382 Value::Int(i64::MAX),
383 Value::Int(i64::MIN),
384 Value::Float(0.5),
385 Value::Null,
386 Value::Bool(true),
387 Value::Bool(false),
388 ]),
389 );
390 m.insert("item", Value::Ref(Ref::new("item", "minecraft:stick")));
391 m.insert("tag", Value::Tag(Ref::new("item", "minecraft:planks")));
392 m.insert(
393 "title",
394 Value::Res(Res {
395 set: "Strings".into(),
396 key: "title".into(),
397 }),
398 );
399 m.insert("text", Value::Text("{b|hi}".into()));
400 m.insert("icon", Value::Image("sprite:x".into()));
401 m.insert("color", Value::Color(0x80FF0000));
402 m.insert("again", Value::Str("Ruby".into()));
403 Value::Map(m)
404 }
405
406 #[test]
407 fn round_trips_every_type() {
408 let v = sample();
409 let bytes = encode(&v);
410 assert_eq!(decode(&bytes).unwrap(), v);
411 assert_eq!(bytes.windows(4).filter(|w| w == b"Ruby").count(), 1);
413 }
414
415 #[test]
416 fn malformed_bytes_are_errors() {
417 let bytes = encode(&sample());
418 for len in 0..bytes.len() {
420 assert!(decode(&bytes[..len]).is_err(), "truncated at {len}");
421 }
422 let mut extra = bytes.clone();
423 extra.push(0);
424 assert!(
425 decode(&extra)
426 .unwrap_err()
427 .message
428 .contains("after the value")
429 );
430 assert!(
431 decode(b"YOGD\x02\x00\x00")
432 .unwrap_err()
433 .message
434 .contains("version 2")
435 );
436 assert!(
437 decode(b"YOGD\x01\x00\x63")
438 .unwrap_err()
439 .message
440 .contains("unknown tag")
441 );
442 assert!(
443 decode(b"YOGD\x01\x00\x05\x00")
444 .unwrap_err()
445 .message
446 .contains("isn't in the table")
447 );
448 assert!(
450 decode(b"YOGD\x01\x00\x06\xff\xff\xff\xff\x0f")
451 .unwrap_err()
452 .message
453 .contains("past the end")
454 );
455 let deep: Vec<u8> = b"YOGD\x01\x00"
456 .iter()
457 .copied()
458 .chain(std::iter::repeat_n([6u8, 1], 300).flatten())
459 .chain([0])
460 .collect();
461 assert!(decode(&deep).unwrap_err().message.contains("nested deeper"));
462 }
463}