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