Skip to main content

nbt_rust/
encoding.rs

1use std::fmt;
2use std::io::{Read, Write};
3use std::marker::PhantomData;
4
5use byteorder::{
6    BigEndian as ByteOrderBigEndian, ByteOrder, LittleEndian as ByteOrderLittleEndian,
7    ReadBytesExt, WriteBytesExt,
8};
9use paste::paste;
10
11use crate::error::{Error, Result};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum EncodingKind {
15    BigEndian,
16    LittleEndian,
17    NetworkLittleEndian,
18}
19
20impl EncodingKind {
21    pub const fn is_network(self) -> bool {
22        matches!(self, Self::NetworkLittleEndian)
23    }
24}
25
26pub trait Encoding: Copy + Clone + Default + fmt::Debug + Send + Sync + 'static {
27    const KIND: EncodingKind;
28
29    fn read_i16<R: Read>(reader: &mut R) -> Result<i16>;
30    fn write_i16<W: Write>(writer: &mut W, value: i16) -> Result<()>;
31
32    fn read_i32<R: Read>(reader: &mut R) -> Result<i32>;
33    fn write_i32<W: Write>(writer: &mut W, value: i32) -> Result<()>;
34
35    fn read_i64<R: Read>(reader: &mut R) -> Result<i64>;
36    fn write_i64<W: Write>(writer: &mut W, value: i64) -> Result<()>;
37
38    fn read_f32<R: Read>(reader: &mut R) -> Result<f32>;
39    fn write_f32<W: Write>(writer: &mut W, value: f32) -> Result<()>;
40
41    fn read_f64<R: Read>(reader: &mut R) -> Result<f64>;
42    fn write_f64<W: Write>(writer: &mut W, value: f64) -> Result<()>;
43
44    fn read_string_len<R: Read>(reader: &mut R) -> Result<usize>;
45    fn write_string_len<W: Write>(writer: &mut W, len: usize) -> Result<()>;
46
47    fn read_list_len<R: Read>(reader: &mut R) -> Result<usize>;
48    fn write_list_len<W: Write>(writer: &mut W, len: usize) -> Result<()>;
49}
50
51#[derive(Debug, Clone, Copy, Default)]
52pub struct BigEndian;
53
54#[derive(Debug, Clone, Copy, Default)]
55pub struct LittleEndian;
56
57#[derive(Debug, Clone, Copy, Default)]
58pub struct NetworkLittleEndian;
59
60#[derive(Debug, Clone, Copy, Default)]
61pub struct Codec<E: Encoding> {
62    _marker: PhantomData<E>,
63}
64
65impl<E: Encoding> Codec<E> {
66    pub const fn new() -> Self {
67        Self {
68            _marker: PhantomData,
69        }
70    }
71
72    pub const fn kind(self) -> EncodingKind {
73        E::KIND
74    }
75
76    pub fn read_i16<R: Read>(self, reader: &mut R) -> Result<i16> {
77        E::read_i16(reader)
78    }
79
80    pub fn write_i16<W: Write>(self, writer: &mut W, value: i16) -> Result<()> {
81        E::write_i16(writer, value)
82    }
83
84    pub fn read_i32<R: Read>(self, reader: &mut R) -> Result<i32> {
85        E::read_i32(reader)
86    }
87
88    pub fn write_i32<W: Write>(self, writer: &mut W, value: i32) -> Result<()> {
89        E::write_i32(writer, value)
90    }
91
92    pub fn read_i64<R: Read>(self, reader: &mut R) -> Result<i64> {
93        E::read_i64(reader)
94    }
95
96    pub fn write_i64<W: Write>(self, writer: &mut W, value: i64) -> Result<()> {
97        E::write_i64(writer, value)
98    }
99
100    pub fn read_f32<R: Read>(self, reader: &mut R) -> Result<f32> {
101        E::read_f32(reader)
102    }
103
104    pub fn write_f32<W: Write>(self, writer: &mut W, value: f32) -> Result<()> {
105        E::write_f32(writer, value)
106    }
107
108    pub fn read_f64<R: Read>(self, reader: &mut R) -> Result<f64> {
109        E::read_f64(reader)
110    }
111
112    pub fn write_f64<W: Write>(self, writer: &mut W, value: f64) -> Result<()> {
113        E::write_f64(writer, value)
114    }
115
116    pub fn read_string_len<R: Read>(self, reader: &mut R) -> Result<usize> {
117        E::read_string_len(reader)
118    }
119
120    pub fn write_string_len<W: Write>(self, writer: &mut W, len: usize) -> Result<()> {
121        E::write_string_len(writer, len)
122    }
123
124    pub fn read_list_len<R: Read>(self, reader: &mut R) -> Result<usize> {
125        E::read_list_len(reader)
126    }
127
128    pub fn write_list_len<W: Write>(self, writer: &mut W, len: usize) -> Result<()> {
129        E::write_list_len(writer, len)
130    }
131}
132
133pub type BigEndianCodec = Codec<BigEndian>;
134pub type LittleEndianCodec = Codec<LittleEndian>;
135pub type NetworkLittleEndianCodec = Codec<NetworkLittleEndian>;
136
137pub const BE: BigEndianCodec = Codec::new();
138pub const LE: LittleEndianCodec = Codec::new();
139pub const NLE: NetworkLittleEndianCodec = Codec::new();
140
141impl Encoding for BigEndian {
142    const KIND: EncodingKind = EncodingKind::BigEndian;
143
144    fn read_i16<R: Read>(reader: &mut R) -> Result<i16> {
145        read_i16_order::<_, ByteOrderBigEndian>(reader)
146    }
147
148    fn write_i16<W: Write>(writer: &mut W, value: i16) -> Result<()> {
149        write_i16_order::<_, ByteOrderBigEndian>(writer, value)
150    }
151
152    fn read_i32<R: Read>(reader: &mut R) -> Result<i32> {
153        read_i32_order::<_, ByteOrderBigEndian>(reader)
154    }
155
156    fn write_i32<W: Write>(writer: &mut W, value: i32) -> Result<()> {
157        write_i32_order::<_, ByteOrderBigEndian>(writer, value)
158    }
159
160    fn read_i64<R: Read>(reader: &mut R) -> Result<i64> {
161        read_i64_order::<_, ByteOrderBigEndian>(reader)
162    }
163
164    fn write_i64<W: Write>(writer: &mut W, value: i64) -> Result<()> {
165        write_i64_order::<_, ByteOrderBigEndian>(writer, value)
166    }
167
168    fn read_f32<R: Read>(reader: &mut R) -> Result<f32> {
169        read_f32_order::<_, ByteOrderBigEndian>(reader)
170    }
171
172    fn write_f32<W: Write>(writer: &mut W, value: f32) -> Result<()> {
173        write_f32_order::<_, ByteOrderBigEndian>(writer, value)
174    }
175
176    fn read_f64<R: Read>(reader: &mut R) -> Result<f64> {
177        read_f64_order::<_, ByteOrderBigEndian>(reader)
178    }
179
180    fn write_f64<W: Write>(writer: &mut W, value: f64) -> Result<()> {
181        write_f64_order::<_, ByteOrderBigEndian>(writer, value)
182    }
183
184    fn read_string_len<R: Read>(reader: &mut R) -> Result<usize> {
185        Ok(read_u16_order::<_, ByteOrderBigEndian>(reader)? as usize)
186    }
187
188    fn write_string_len<W: Write>(writer: &mut W, len: usize) -> Result<()> {
189        ensure_fit("string_length", len, u16::MAX as usize)?;
190        write_u16_order::<_, ByteOrderBigEndian>(writer, len as u16)
191    }
192
193    fn read_list_len<R: Read>(reader: &mut R) -> Result<usize> {
194        let len = read_i32_order::<_, ByteOrderBigEndian>(reader)?;
195        non_negative_len("list_length", len)
196    }
197
198    fn write_list_len<W: Write>(writer: &mut W, len: usize) -> Result<()> {
199        ensure_fit("list_length", len, i32::MAX as usize)?;
200        write_i32_order::<_, ByteOrderBigEndian>(writer, len as i32)
201    }
202}
203
204impl Encoding for LittleEndian {
205    const KIND: EncodingKind = EncodingKind::LittleEndian;
206
207    fn read_i16<R: Read>(reader: &mut R) -> Result<i16> {
208        read_i16_order::<_, ByteOrderLittleEndian>(reader)
209    }
210
211    fn write_i16<W: Write>(writer: &mut W, value: i16) -> Result<()> {
212        write_i16_order::<_, ByteOrderLittleEndian>(writer, value)
213    }
214
215    fn read_i32<R: Read>(reader: &mut R) -> Result<i32> {
216        read_i32_order::<_, ByteOrderLittleEndian>(reader)
217    }
218
219    fn write_i32<W: Write>(writer: &mut W, value: i32) -> Result<()> {
220        write_i32_order::<_, ByteOrderLittleEndian>(writer, value)
221    }
222
223    fn read_i64<R: Read>(reader: &mut R) -> Result<i64> {
224        read_i64_order::<_, ByteOrderLittleEndian>(reader)
225    }
226
227    fn write_i64<W: Write>(writer: &mut W, value: i64) -> Result<()> {
228        write_i64_order::<_, ByteOrderLittleEndian>(writer, value)
229    }
230
231    fn read_f32<R: Read>(reader: &mut R) -> Result<f32> {
232        read_f32_order::<_, ByteOrderLittleEndian>(reader)
233    }
234
235    fn write_f32<W: Write>(writer: &mut W, value: f32) -> Result<()> {
236        write_f32_order::<_, ByteOrderLittleEndian>(writer, value)
237    }
238
239    fn read_f64<R: Read>(reader: &mut R) -> Result<f64> {
240        read_f64_order::<_, ByteOrderLittleEndian>(reader)
241    }
242
243    fn write_f64<W: Write>(writer: &mut W, value: f64) -> Result<()> {
244        write_f64_order::<_, ByteOrderLittleEndian>(writer, value)
245    }
246
247    fn read_string_len<R: Read>(reader: &mut R) -> Result<usize> {
248        Ok(read_u16_order::<_, ByteOrderLittleEndian>(reader)? as usize)
249    }
250
251    fn write_string_len<W: Write>(writer: &mut W, len: usize) -> Result<()> {
252        ensure_fit("string_length", len, u16::MAX as usize)?;
253        write_u16_order::<_, ByteOrderLittleEndian>(writer, len as u16)
254    }
255
256    fn read_list_len<R: Read>(reader: &mut R) -> Result<usize> {
257        let len = read_i32_order::<_, ByteOrderLittleEndian>(reader)?;
258        non_negative_len("list_length", len)
259    }
260
261    fn write_list_len<W: Write>(writer: &mut W, len: usize) -> Result<()> {
262        ensure_fit("list_length", len, i32::MAX as usize)?;
263        write_i32_order::<_, ByteOrderLittleEndian>(writer, len as i32)
264    }
265}
266
267impl Encoding for NetworkLittleEndian {
268    const KIND: EncodingKind = EncodingKind::NetworkLittleEndian;
269
270    fn read_i16<R: Read>(reader: &mut R) -> Result<i16> {
271        read_i16_order::<_, ByteOrderLittleEndian>(reader)
272    }
273
274    fn write_i16<W: Write>(writer: &mut W, value: i16) -> Result<()> {
275        write_i16_order::<_, ByteOrderLittleEndian>(writer, value)
276    }
277
278    fn read_i32<R: Read>(reader: &mut R) -> Result<i32> {
279        read_var_i32(reader)
280    }
281
282    fn write_i32<W: Write>(writer: &mut W, value: i32) -> Result<()> {
283        write_var_i32(writer, value)
284    }
285
286    fn read_i64<R: Read>(reader: &mut R) -> Result<i64> {
287        read_var_i64(reader)
288    }
289
290    fn write_i64<W: Write>(writer: &mut W, value: i64) -> Result<()> {
291        write_var_i64(writer, value)
292    }
293
294    fn read_f32<R: Read>(reader: &mut R) -> Result<f32> {
295        read_f32_order::<_, ByteOrderLittleEndian>(reader)
296    }
297
298    fn write_f32<W: Write>(writer: &mut W, value: f32) -> Result<()> {
299        write_f32_order::<_, ByteOrderLittleEndian>(writer, value)
300    }
301
302    fn read_f64<R: Read>(reader: &mut R) -> Result<f64> {
303        read_f64_order::<_, ByteOrderLittleEndian>(reader)
304    }
305
306    fn write_f64<W: Write>(writer: &mut W, value: f64) -> Result<()> {
307        write_f64_order::<_, ByteOrderLittleEndian>(writer, value)
308    }
309
310    fn read_string_len<R: Read>(reader: &mut R) -> Result<usize> {
311        let len = read_var_u32(reader)? as usize;
312        ensure_fit("string_length", len, i32::MAX as usize)?;
313        Ok(len)
314    }
315
316    fn write_string_len<W: Write>(writer: &mut W, len: usize) -> Result<()> {
317        ensure_fit("string_length", len, i32::MAX as usize)?;
318        write_var_u32(writer, len as u32)
319    }
320
321    fn read_list_len<R: Read>(reader: &mut R) -> Result<usize> {
322        let len = read_var_u32(reader)? as usize;
323        ensure_fit("list_length", len, i32::MAX as usize)?;
324        Ok(len)
325    }
326
327    fn write_list_len<W: Write>(writer: &mut W, len: usize) -> Result<()> {
328        ensure_fit("list_length", len, i32::MAX as usize)?;
329        write_var_u32(writer, len as u32)
330    }
331}
332
333pub fn encode_zigzag_i32(value: i32) -> u32 {
334    ((value << 1) ^ (value >> 31)) as u32
335}
336
337pub fn decode_zigzag_i32(value: u32) -> i32 {
338    ((value >> 1) as i32) ^ -((value & 1) as i32)
339}
340
341pub fn encode_zigzag_i64(value: i64) -> u64 {
342    ((value << 1) ^ (value >> 63)) as u64
343}
344
345pub fn decode_zigzag_i64(value: u64) -> i64 {
346    ((value >> 1) as i64) ^ -((value & 1) as i64)
347}
348
349pub fn read_var_u32<R: Read>(reader: &mut R) -> Result<u32> {
350    let mut value = 0u32;
351    let mut shift = 0u32;
352
353    for index in 0..5u8 {
354        let byte = match read_u8(reader) {
355            Ok(byte) => byte,
356            Err(Error::Io(io_error)) if io_error.kind() == std::io::ErrorKind::UnexpectedEof => {
357                return Err(Error::InvalidVarint {
358                    detail: "truncated u32 varint",
359                });
360            }
361            Err(error) => return Err(error),
362        };
363        value |= ((byte & 0x7F) as u32) << shift;
364        if byte & 0x80 == 0 {
365            return Ok(value);
366        }
367        shift += 7;
368        if index == 4 {
369            return Err(Error::InvalidVarint {
370                detail: "u32 varint exceeds 5 bytes",
371            });
372        }
373    }
374
375    Err(Error::InvalidVarint {
376        detail: "failed to decode u32 varint",
377    })
378}
379
380pub fn write_var_u32<W: Write>(writer: &mut W, mut value: u32) -> Result<()> {
381    loop {
382        let mut byte = (value & 0x7F) as u8;
383        value >>= 7;
384        if value != 0 {
385            byte |= 0x80;
386        }
387        writer.write_all(&[byte])?;
388        if value == 0 {
389            return Ok(());
390        }
391    }
392}
393
394pub fn read_var_u64<R: Read>(reader: &mut R) -> Result<u64> {
395    let mut value = 0u64;
396    let mut shift = 0u32;
397
398    for index in 0..10u8 {
399        let byte = match read_u8(reader) {
400            Ok(byte) => byte,
401            Err(Error::Io(io_error)) if io_error.kind() == std::io::ErrorKind::UnexpectedEof => {
402                return Err(Error::InvalidVarint {
403                    detail: "truncated u64 varint",
404                });
405            }
406            Err(error) => return Err(error),
407        };
408        if index == 9 {
409            if byte & 0x80 != 0 {
410                return Err(Error::InvalidVarint {
411                    detail: "u64 varint exceeds 10 bytes",
412                });
413            }
414            if byte > 0x01 {
415                return Err(Error::InvalidVarint {
416                    detail: "u64 varint terminal byte overflow",
417                });
418            }
419        }
420        value |= ((byte & 0x7F) as u64) << shift;
421        if byte & 0x80 == 0 {
422            return Ok(value);
423        }
424        shift += 7;
425        if index == 9 {
426            return Err(Error::InvalidVarint {
427                detail: "u64 varint exceeds 10 bytes",
428            });
429        }
430    }
431
432    Err(Error::InvalidVarint {
433        detail: "failed to decode u64 varint",
434    })
435}
436
437pub fn write_var_u64<W: Write>(writer: &mut W, mut value: u64) -> Result<()> {
438    loop {
439        let mut byte = (value & 0x7F) as u8;
440        value >>= 7;
441        if value != 0 {
442            byte |= 0x80;
443        }
444        writer.write_all(&[byte])?;
445        if value == 0 {
446            return Ok(());
447        }
448    }
449}
450
451pub fn read_var_i32<R: Read>(reader: &mut R) -> Result<i32> {
452    let raw = read_var_u32(reader)?;
453    Ok(decode_zigzag_i32(raw))
454}
455
456pub fn write_var_i32<W: Write>(writer: &mut W, value: i32) -> Result<()> {
457    write_var_u32(writer, encode_zigzag_i32(value))
458}
459
460pub fn read_var_i64<R: Read>(reader: &mut R) -> Result<i64> {
461    let raw = read_var_u64(reader)?;
462    Ok(decode_zigzag_i64(raw))
463}
464
465pub fn write_var_i64<W: Write>(writer: &mut W, value: i64) -> Result<()> {
466    write_var_u64(writer, encode_zigzag_i64(value))
467}
468
469fn read_u8<R: Read>(reader: &mut R) -> Result<u8> {
470    let mut byte = [0u8; 1];
471    reader.read_exact(&mut byte)?;
472    Ok(byte[0])
473}
474
475macro_rules! define_ordered_rw {
476    ($(($suffix:ident, $ty:ty)),+ $(,)?) => {
477        paste! {
478            $(
479                fn [<read_ $suffix _order>]<R: Read, O: ByteOrder>(
480                    reader: &mut R,
481                ) -> Result<$ty> {
482                    reader.[<read_ $suffix>]::<O>().map_err(Error::from)
483                }
484
485                fn [<write_ $suffix _order>]<W: Write, O: ByteOrder>(
486                    writer: &mut W,
487                    value: $ty,
488                ) -> Result<()> {
489                    writer.[<write_ $suffix>]::<O>(value).map_err(Error::from)
490                }
491            )+
492        }
493    };
494}
495
496define_ordered_rw!(
497    (u16, u16),
498    (i16, i16),
499    (i32, i32),
500    (i64, i64),
501    (f32, f32),
502    (f64, f64)
503);
504
505fn ensure_fit(field: &'static str, actual: usize, max: usize) -> Result<()> {
506    if actual > max {
507        return Err(Error::LengthOverflow { field, max, actual });
508    }
509    Ok(())
510}
511
512fn non_negative_len(field: &'static str, value: i32) -> Result<usize> {
513    if value < 0 {
514        return Err(Error::NegativeLength { field, value });
515    }
516    Ok(value as usize)
517}
518
519#[cfg(test)]
520mod tests {
521    use std::io::Cursor;
522
523    use super::*;
524
525    #[test]
526    fn be_uses_fixed_width_i32() {
527        let mut out = Vec::new();
528        BE.write_i32(&mut out, 300).unwrap();
529        assert_eq!(out, vec![0x00, 0x00, 0x01, 0x2c]);
530    }
531
532    #[test]
533    fn le_uses_fixed_width_i32() {
534        let mut out = Vec::new();
535        LE.write_i32(&mut out, 300).unwrap();
536        assert_eq!(out, vec![0x2c, 0x01, 0x00, 0x00]);
537    }
538
539    #[test]
540    fn nle_i32_roundtrip_uses_zigzag_varint() {
541        let values = [0, 1, -1, 2, -2, i32::MAX, i32::MIN];
542        for value in values {
543            let mut out = Vec::new();
544            NLE.write_i32(&mut out, value).unwrap();
545            let mut input = Cursor::new(out);
546            let decoded = NLE.read_i32(&mut input).unwrap();
547            assert_eq!(decoded, value);
548        }
549    }
550
551    #[test]
552    fn nle_i64_roundtrip_uses_zigzag_varint() {
553        let values = [0, 1, -1, 2, -2, i64::MAX, i64::MIN];
554        for value in values {
555            let mut out = Vec::new();
556            NLE.write_i64(&mut out, value).unwrap();
557            let mut input = Cursor::new(out);
558            let decoded = NLE.read_i64(&mut input).unwrap();
559            assert_eq!(decoded, value);
560        }
561    }
562
563    #[test]
564    fn nle_keeps_float_as_little_endian() {
565        let mut out = Vec::new();
566        NLE.write_f32(&mut out, 1.5).unwrap();
567        assert_eq!(out, 1.5f32.to_le_bytes());
568    }
569
570    #[test]
571    fn nle_uses_varuint_for_string_and_list_lengths() {
572        let mut out = Vec::new();
573        NLE.write_string_len(&mut out, 300).unwrap();
574        NLE.write_list_len(&mut out, 300).unwrap();
575
576        // varuint(300) = 0xAC, 0x02
577        assert_eq!(out, vec![0xAC, 0x02, 0xAC, 0x02]);
578
579        let mut input = Cursor::new(out);
580        assert_eq!(NLE.read_string_len(&mut input).unwrap(), 300);
581        assert_eq!(NLE.read_list_len(&mut input).unwrap(), 300);
582    }
583
584    #[test]
585    fn be_string_len_uses_u16_prefix() {
586        let mut out = Vec::new();
587        BE.write_string_len(&mut out, 300).unwrap();
588        assert_eq!(out, vec![0x01, 0x2C]);
589
590        let mut input = Cursor::new(out);
591        assert_eq!(BE.read_string_len(&mut input).unwrap(), 300);
592    }
593
594    #[test]
595    fn le_string_len_uses_u16_prefix() {
596        let mut out = Vec::new();
597        LE.write_string_len(&mut out, 300).unwrap();
598        assert_eq!(out, vec![0x2C, 0x01]);
599
600        let mut input = Cursor::new(out);
601        assert_eq!(LE.read_string_len(&mut input).unwrap(), 300);
602    }
603
604    #[test]
605    fn be_list_len_uses_i32_prefix() {
606        let mut out = Vec::new();
607        BE.write_list_len(&mut out, 300).unwrap();
608        assert_eq!(out, vec![0x00, 0x00, 0x01, 0x2C]);
609
610        let mut input = Cursor::new(out);
611        assert_eq!(BE.read_list_len(&mut input).unwrap(), 300);
612    }
613
614    #[test]
615    fn le_list_len_uses_i32_prefix() {
616        let mut out = Vec::new();
617        LE.write_list_len(&mut out, 300).unwrap();
618        assert_eq!(out, vec![0x2C, 0x01, 0x00, 0x00]);
619
620        let mut input = Cursor::new(out);
621        assert_eq!(LE.read_list_len(&mut input).unwrap(), 300);
622    }
623
624    #[test]
625    fn be_string_len_overflow_is_rejected() {
626        let err = BE.write_string_len(&mut Vec::new(), (u16::MAX as usize) + 1);
627        assert!(matches!(err, Err(Error::LengthOverflow { .. })));
628    }
629
630    #[test]
631    fn be_negative_list_len_is_rejected() {
632        let mut input = Cursor::new((-1i32).to_be_bytes());
633        let err = BE.read_list_len(&mut input);
634        assert!(matches!(err, Err(Error::NegativeLength { .. })));
635    }
636
637    #[test]
638    fn overlong_var_u32_is_rejected() {
639        let bytes = vec![0x80, 0x80, 0x80, 0x80, 0x80, 0x00];
640        let mut input = Cursor::new(bytes);
641        let err = read_var_u32(&mut input);
642        assert!(matches!(err, Err(Error::InvalidVarint { .. })));
643    }
644
645    #[test]
646    fn overlong_var_u64_is_rejected() {
647        let bytes = vec![
648            0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
649        ];
650        let mut input = Cursor::new(bytes);
651        let err = read_var_u64(&mut input);
652        assert!(matches!(err, Err(Error::InvalidVarint { .. })));
653    }
654
655    #[test]
656    fn invalid_terminal_byte_var_u64_is_rejected() {
657        // 10-byte payload where the terminal byte carries bits beyond bit 63.
658        let bytes = vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02];
659        let mut input = Cursor::new(bytes);
660        let err = read_var_u64(&mut input);
661        assert!(matches!(
662            err,
663            Err(Error::InvalidVarint {
664                detail: "u64 varint terminal byte overflow"
665            })
666        ));
667    }
668
669    #[test]
670    fn truncated_var_u32_is_rejected_as_invalid_varint() {
671        let mut input = Cursor::new(vec![0x80]);
672        let err = read_var_u32(&mut input);
673        assert!(matches!(err, Err(Error::InvalidVarint { .. })));
674    }
675
676    #[test]
677    fn truncated_var_u64_is_rejected_as_invalid_varint() {
678        let mut input = Cursor::new(vec![0x80]);
679        let err = read_var_u64(&mut input);
680        assert!(matches!(err, Err(Error::InvalidVarint { .. })));
681    }
682
683    #[test]
684    fn var_u64_boundary_values_roundtrip() {
685        let values = [
686            0u64,
687            1,
688            127,
689            128,
690            255,
691            16_383,
692            16_384,
693            u32::MAX as u64,
694            (u32::MAX as u64) + 1,
695            (1u64 << 63) - 1,
696            1u64 << 63,
697            u64::MAX,
698        ];
699
700        for value in values {
701            let mut out = Vec::new();
702            write_var_u64(&mut out, value).unwrap();
703            let mut input = Cursor::new(out);
704            let decoded = read_var_u64(&mut input).unwrap();
705            assert_eq!(decoded, value);
706        }
707    }
708
709    #[test]
710    fn nle_keeps_i16_as_little_endian_fixed_width() {
711        let mut out = Vec::new();
712        NLE.write_i16(&mut out, 0x1234).unwrap();
713        assert_eq!(out, 0x1234i16.to_le_bytes());
714
715        let mut input = Cursor::new(out);
716        let value = NLE.read_i16(&mut input).unwrap();
717        assert_eq!(value, 0x1234i16);
718    }
719
720    #[test]
721    fn nle_keeps_f64_as_little_endian() {
722        let mut out = Vec::new();
723        NLE.write_f64(&mut out, 123.456).unwrap();
724        assert_eq!(out, 123.456f64.to_le_bytes());
725
726        let mut input = Cursor::new(out);
727        let value = NLE.read_f64(&mut input).unwrap();
728        assert_eq!(value, 123.456f64);
729    }
730
731    #[test]
732    fn nle_read_string_len_over_i32_max_is_rejected() {
733        // varuint(2_147_483_648) => 0x80 0x80 0x80 0x80 0x08
734        let mut input = Cursor::new(vec![0x80, 0x80, 0x80, 0x80, 0x08]);
735        let err = NLE.read_string_len(&mut input);
736        assert!(matches!(err, Err(Error::LengthOverflow { .. })));
737    }
738
739    #[test]
740    fn nle_write_list_len_over_i32_max_is_rejected() {
741        let err = NLE.write_list_len(&mut Vec::new(), (i32::MAX as usize) + 1);
742        assert!(matches!(err, Err(Error::LengthOverflow { .. })));
743    }
744}