Skip to main content

ts_wire/
msgpack.rs

1// Copyright (c) 2026 LightSeek Foundation
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! msgpack primitives that make msgspec's encoding choices.
22//!
23//! The Python side encodes with `msgspec`, which always picks the smallest
24//! integer, string, binary, array, map and ext format and writes every float
25//! as float64. [`Encoder`] makes the same choices, so a message decoded and
26//! re-encoded here is byte-identical to the Python encoding. [`Decoder`]
27//! accepts every msgpack format, as msgspec's decoder does.
28//!
29//! A message is a list of ZMQ frames: frame 0 is the msgpack buffer, and
30//! large tensors ride in further frames that the buffer references by index
31//! (see [`crate::tensor`]).
32
33use bytes::Bytes;
34
35use crate::error::{Error, Result};
36
37/// Tensors smaller than this many bytes are inlined in the msgpack buffer;
38/// larger ones become their own frame. Matches `MSGPACK_ZERO_COPY_THRESHOLD`.
39pub const INLINE_THRESHOLD: usize = 256;
40
41/// Writes one message: the msgpack buffer plus out-of-band frames.
42#[derive(Debug)]
43pub struct Encoder {
44    buf: Vec<u8>,
45    frames: Vec<Bytes>,
46    inline_threshold: usize,
47}
48
49impl Default for Encoder {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl Encoder {
56    /// An encoder with the Python side's inline threshold.
57    pub fn new() -> Self {
58        Self::with_inline_threshold(INLINE_THRESHOLD)
59    }
60
61    /// An encoder that frames out buffers of at least `inline_threshold` bytes.
62    pub fn with_inline_threshold(inline_threshold: usize) -> Self {
63        Self {
64            buf: Vec::new(),
65            frames: Vec::new(),
66            inline_threshold,
67        }
68    }
69
70    /// Finish the message: the msgpack buffer, then the out-of-band frames.
71    pub fn into_frames(self) -> Vec<Bytes> {
72        let mut frames = Vec::with_capacity(1 + self.frames.len());
73        frames.push(Bytes::from(self.buf));
74        frames.extend(self.frames);
75        frames
76    }
77
78    /// Finish a message that has no out-of-band frames.
79    ///
80    /// # Panics
81    ///
82    /// If a buffer was framed out: the caller would drop it from the wire.
83    pub fn into_single_frame(self) -> Vec<u8> {
84        assert!(
85            self.frames.is_empty(),
86            "message has {} out-of-band frames; use into_frames",
87            self.frames.len()
88        );
89        self.buf
90    }
91
92    /// Write `nil`.
93    pub fn nil(&mut self) {
94        self.buf.push(0xc0);
95    }
96
97    /// Write a boolean.
98    pub fn bool(&mut self, value: bool) {
99        self.buf.push(if value { 0xc3 } else { 0xc2 });
100    }
101
102    /// Write a signed integer: non-negative values use the unsigned formats.
103    pub fn int(&mut self, value: i64) {
104        if value >= 0 {
105            self.uint(value as u64);
106        } else if value >= -32 {
107            self.buf.push(value as u8);
108        } else if value >= i64::from(i8::MIN) {
109            self.buf.push(0xd0);
110            self.buf.push(value as i8 as u8);
111        } else if value >= i64::from(i16::MIN) {
112            self.buf.push(0xd1);
113            self.buf.extend_from_slice(&(value as i16).to_be_bytes());
114        } else if value >= i64::from(i32::MIN) {
115            self.buf.push(0xd2);
116            self.buf.extend_from_slice(&(value as i32).to_be_bytes());
117        } else {
118            self.buf.push(0xd3);
119            self.buf.extend_from_slice(&value.to_be_bytes());
120        }
121    }
122
123    /// Write an unsigned integer.
124    pub fn uint(&mut self, value: u64) {
125        if value < 0x80 {
126            self.buf.push(value as u8);
127        } else if value <= u64::from(u8::MAX) {
128            self.buf.push(0xcc);
129            self.buf.push(value as u8);
130        } else if value <= u64::from(u16::MAX) {
131            self.buf.push(0xcd);
132            self.buf.extend_from_slice(&(value as u16).to_be_bytes());
133        } else if value <= u64::from(u32::MAX) {
134            self.buf.push(0xce);
135            self.buf.extend_from_slice(&(value as u32).to_be_bytes());
136        } else {
137            self.buf.push(0xcf);
138            self.buf.extend_from_slice(&value.to_be_bytes());
139        }
140    }
141
142    /// Write a float, always as float64.
143    pub fn float(&mut self, value: f64) {
144        self.buf.push(0xcb);
145        self.buf.extend_from_slice(&value.to_be_bytes());
146    }
147
148    /// Write a string.
149    pub fn str(&mut self, value: &str) {
150        let len = value.len();
151        if len < 32 {
152            self.buf.push(0xa0 | len as u8);
153        } else {
154            self.len_header(len, [0xd9, 0xda, 0xdb]);
155        }
156        self.buf.extend_from_slice(value.as_bytes());
157    }
158
159    /// Write a binary value.
160    pub fn bin(&mut self, value: &[u8]) {
161        self.len_header(value.len(), [0xc4, 0xc5, 0xc6]);
162        self.buf.extend_from_slice(value);
163    }
164
165    /// Write an array header for `len` elements.
166    pub fn array_len(&mut self, len: usize) {
167        if len < 16 {
168            self.buf.push(0x90 | len as u8);
169        } else {
170            self.len_header16(len, [0xdc, 0xdd]);
171        }
172    }
173
174    /// Write a map header for `len` entries.
175    pub fn map_len(&mut self, len: usize) {
176        if len < 16 {
177            self.buf.push(0x80 | len as u8);
178        } else {
179            self.len_header16(len, [0xde, 0xdf]);
180        }
181    }
182
183    /// Write an extension value.
184    pub fn ext(&mut self, code: i8, data: &[u8]) {
185        let fixed = match data.len() {
186            1 => Some(0xd4),
187            2 => Some(0xd5),
188            4 => Some(0xd6),
189            8 => Some(0xd7),
190            16 => Some(0xd8),
191            _ => None,
192        };
193        match fixed {
194            Some(marker) => self.buf.push(marker),
195            None => self.len_header(data.len(), [0xc7, 0xc8, 0xc9]),
196        }
197        self.buf.push(code as u8);
198        self.buf.extend_from_slice(data);
199    }
200
201    /// Write `data` either inline as an ext value or as a reference to a new
202    /// out-of-band frame, by size.
203    pub(crate) fn buffer(&mut self, ext_code: i8, data: &Bytes) {
204        if data.len() < self.inline_threshold {
205            self.ext(ext_code, data);
206        } else {
207            self.frames.push(data.clone());
208            // Frame 0 is the msgpack buffer, so the first frame is index 1.
209            self.uint(self.frames.len() as u64);
210        }
211    }
212
213    fn len_header(&mut self, len: usize, markers: [u8; 3]) {
214        if len <= usize::from(u8::MAX) {
215            self.buf.push(markers[0]);
216            self.buf.push(len as u8);
217        } else {
218            self.len_header16(len, [markers[1], markers[2]]);
219        }
220    }
221
222    fn len_header16(&mut self, len: usize, markers: [u8; 2]) {
223        if len <= usize::from(u16::MAX) {
224            self.buf.push(markers[0]);
225            self.buf.extend_from_slice(&(len as u16).to_be_bytes());
226        } else {
227            let len = u32::try_from(len).expect("msgpack lengths are at most u32::MAX");
228            self.buf.push(markers[1]);
229            self.buf.extend_from_slice(&len.to_be_bytes());
230        }
231    }
232}
233
234/// The msgpack type of the next value, for decoding unions.
235#[derive(Debug, Clone, Copy, PartialEq, Eq)]
236pub enum Kind {
237    /// `nil`
238    Nil,
239    /// `true` / `false`
240    Bool,
241    /// Any integer format.
242    Int,
243    /// float32 or float64.
244    Float,
245    /// A string.
246    Str,
247    /// A binary value.
248    Bin,
249    /// An array.
250    Array,
251    /// A map.
252    Map,
253    /// An extension value.
254    Ext,
255}
256
257impl Kind {
258    fn of(marker: u8) -> Option<Self> {
259        Some(match marker {
260            0x00..=0x7f | 0xcc..=0xd3 | 0xe0..=0xff => Kind::Int,
261            0x80..=0x8f | 0xde | 0xdf => Kind::Map,
262            0x90..=0x9f | 0xdc | 0xdd => Kind::Array,
263            0xa0..=0xbf | 0xd9..=0xdb => Kind::Str,
264            0xc0 => Kind::Nil,
265            0xc2 | 0xc3 => Kind::Bool,
266            0xc4..=0xc6 => Kind::Bin,
267            0xc7..=0xc9 | 0xd4..=0xd8 => Kind::Ext,
268            0xca | 0xcb => Kind::Float,
269            0xc1 => return None,
270        })
271    }
272
273    fn name(self) -> &'static str {
274        match self {
275            Kind::Nil => "nil",
276            Kind::Bool => "bool",
277            Kind::Int => "int",
278            Kind::Float => "float",
279            Kind::Str => "str",
280            Kind::Bin => "bin",
281            Kind::Array => "array",
282            Kind::Map => "map",
283            Kind::Ext => "ext",
284        }
285    }
286}
287
288/// An integer as read from the wire, before narrowing to a schema type.
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub enum Integer {
291    /// A value from a signed format, or any value below zero.
292    Signed(i64),
293    /// A value from an unsigned format.
294    Unsigned(u64),
295}
296
297impl Integer {
298    fn wide(self) -> i128 {
299        match self {
300            Integer::Signed(value) => i128::from(value),
301            Integer::Unsigned(value) => i128::from(value),
302        }
303    }
304
305    /// Narrow to any integer type, failing loudly on overflow.
306    pub fn narrow<T: TryFrom<i128>>(self, target: &'static str) -> Result<T> {
307        let value = self.wide();
308        T::try_from(value).map_err(|_| Error::IntRange { value, target })
309    }
310}
311
312/// Reads one message from its frames.
313#[derive(Debug, Clone)]
314pub struct Decoder<'f> {
315    frames: &'f [Bytes],
316    pos: usize,
317}
318
319impl<'f> Decoder<'f> {
320    /// A decoder over a message's frames (frame 0 is the msgpack buffer).
321    ///
322    /// # Panics
323    ///
324    /// If `frames` is empty.
325    pub fn new(frames: &'f [Bytes]) -> Self {
326        assert!(!frames.is_empty(), "a message has at least one frame");
327        Self { frames, pos: 0 }
328    }
329
330    fn buf(&self) -> &'f Bytes {
331        &self.frames[0]
332    }
333
334    /// Fail unless the whole msgpack buffer was consumed.
335    pub fn finish(&self) -> Result<()> {
336        let left = self.buf().len() - self.pos;
337        if left == 0 {
338            Ok(())
339        } else {
340            Err(Error::TrailingBytes(left))
341        }
342    }
343
344    /// The out-of-band frame a buffer reference names.
345    pub(crate) fn frame(&self, index: Integer) -> Result<Bytes> {
346        let index: usize = index.narrow("frame index")?;
347        if index == 0 || index >= self.frames.len() {
348            return Err(Error::Invalid(format!(
349                "frame index {index} out of range: the message has {} out-of-band frames",
350                self.frames.len() - 1
351            )));
352        }
353        Ok(self.frames[index].clone())
354    }
355
356    /// The tag of the tagged array struct that comes next, without
357    /// consuming it.
358    pub fn peek_tag(&self) -> Result<&'f str> {
359        let mut probe = self.clone();
360        if probe.array_len()? == 0 {
361            return Err(Error::MissingField("_tag"));
362        }
363        probe.str()
364    }
365
366    /// The type of the next value, without consuming it.
367    pub fn peek_kind(&self) -> Result<Kind> {
368        let marker = self.peek_marker()?;
369        Kind::of(marker)
370            .ok_or_else(|| Error::Invalid(format!("reserved marker 0xc1 at offset {}", self.pos)))
371    }
372
373    /// Consume a `nil` if the next value is one.
374    pub fn take_nil(&mut self) -> Result<bool> {
375        if self.peek_marker()? == 0xc0 {
376            self.pos += 1;
377            Ok(true)
378        } else {
379            Ok(false)
380        }
381    }
382
383    /// Read a boolean.
384    pub fn bool(&mut self) -> Result<bool> {
385        match self.peek_marker()? {
386            0xc2 => {
387                self.pos += 1;
388                Ok(false)
389            }
390            0xc3 => {
391                self.pos += 1;
392                Ok(true)
393            }
394            _ => Err(self.type_error("bool")),
395        }
396    }
397
398    /// Read an integer in any format.
399    pub fn integer(&mut self) -> Result<Integer> {
400        let marker = self.peek_marker()?;
401        let start = self.pos;
402        let value = match marker {
403            0x00..=0x7f => {
404                self.pos += 1;
405                Integer::Unsigned(u64::from(marker))
406            }
407            0xe0..=0xff => {
408                self.pos += 1;
409                Integer::Signed(i64::from(marker as i8))
410            }
411            0xcc => Integer::Unsigned(u64::from(self.be::<1>(start)?[0])),
412            0xcd => Integer::Unsigned(u64::from(u16::from_be_bytes(self.be(start)?))),
413            0xce => Integer::Unsigned(u64::from(u32::from_be_bytes(self.be(start)?))),
414            0xcf => Integer::Unsigned(u64::from_be_bytes(self.be(start)?)),
415            0xd0 => Integer::Signed(i64::from(self.be::<1>(start)?[0] as i8)),
416            0xd1 => Integer::Signed(i64::from(i16::from_be_bytes(self.be(start)?))),
417            0xd2 => Integer::Signed(i64::from(i32::from_be_bytes(self.be(start)?))),
418            0xd3 => Integer::Signed(i64::from_be_bytes(self.be(start)?)),
419            _ => return Err(self.type_error("int")),
420        };
421        Ok(value)
422    }
423
424    /// Read a float. Integers are accepted, as msgspec does for float fields.
425    pub fn float(&mut self) -> Result<f64> {
426        let start = self.pos;
427        match self.peek_marker()? {
428            0xca => Ok(f64::from(f32::from_be_bytes(self.be(start)?))),
429            0xcb => Ok(f64::from_be_bytes(self.be(start)?)),
430            marker if Kind::of(marker) == Some(Kind::Int) => Ok(self.integer()?.wide() as f64),
431            _ => Err(self.type_error("float")),
432        }
433    }
434
435    /// Read a string, borrowed from the msgpack buffer.
436    pub fn str(&mut self) -> Result<&'f str> {
437        let marker = self.peek_marker()?;
438        let start = self.pos;
439        let len = match marker {
440            0xa0..=0xbf => {
441                self.pos += 1;
442                usize::from(marker & 0x1f)
443            }
444            0xd9 => usize::from(self.be::<1>(start)?[0]),
445            0xda => usize::from(u16::from_be_bytes(self.be(start)?)),
446            0xdb => u32::from_be_bytes(self.be(start)?) as usize,
447            _ => return Err(self.type_error("str")),
448        };
449        let bytes = self.take(start, len)?;
450        std::str::from_utf8(bytes).map_err(|_| Error::Utf8 { offset: start })
451    }
452
453    /// Read a binary value, sharing the msgpack buffer.
454    pub fn bin(&mut self) -> Result<Bytes> {
455        let marker = self.peek_marker()?;
456        let start = self.pos;
457        let len = match marker {
458            0xc4 => usize::from(self.be::<1>(start)?[0]),
459            0xc5 => usize::from(u16::from_be_bytes(self.be(start)?)),
460            0xc6 => u32::from_be_bytes(self.be(start)?) as usize,
461            _ => return Err(self.type_error("bin")),
462        };
463        self.take_shared(start, len)
464    }
465
466    /// Read an array header and return its length.
467    pub fn array_len(&mut self) -> Result<usize> {
468        let marker = self.peek_marker()?;
469        let start = self.pos;
470        match marker {
471            0x90..=0x9f => {
472                self.pos += 1;
473                Ok(usize::from(marker & 0x0f))
474            }
475            0xdc => Ok(usize::from(u16::from_be_bytes(self.be(start)?))),
476            0xdd => Ok(u32::from_be_bytes(self.be(start)?) as usize),
477            _ => Err(self.type_error("array")),
478        }
479    }
480
481    /// Read a map header and return its number of entries.
482    pub fn map_len(&mut self) -> Result<usize> {
483        let marker = self.peek_marker()?;
484        let start = self.pos;
485        match marker {
486            0x80..=0x8f => {
487                self.pos += 1;
488                Ok(usize::from(marker & 0x0f))
489            }
490            0xde => Ok(usize::from(u16::from_be_bytes(self.be(start)?))),
491            0xdf => Ok(u32::from_be_bytes(self.be(start)?) as usize),
492            _ => Err(self.type_error("map")),
493        }
494    }
495
496    /// Read an extension value, sharing the msgpack buffer.
497    pub fn ext(&mut self) -> Result<(i8, Bytes)> {
498        let marker = self.peek_marker()?;
499        let start = self.pos;
500        let len = match marker {
501            0xd4 => 1,
502            0xd5 => 2,
503            0xd6 => 4,
504            0xd7 => 8,
505            0xd8 => 16,
506            0xc7 => usize::from(self.be::<1>(start)?[0]),
507            0xc8 => usize::from(u16::from_be_bytes(self.be(start)?)),
508            0xc9 => u32::from_be_bytes(self.be(start)?) as usize,
509            _ => return Err(self.type_error("ext")),
510        };
511        if (0xd4..=0xd8).contains(&marker) {
512            self.pos += 1;
513        }
514        let code = self.take(start, 1)?[0] as i8;
515        Ok((code, self.take_shared(start, len)?))
516    }
517
518    /// Skip one value of any type.
519    pub fn skip(&mut self) -> Result<()> {
520        let mut pending: usize = 1;
521        while pending > 0 {
522            pending -= 1;
523            match self.peek_kind()? {
524                Kind::Nil | Kind::Bool => self.pos += 1,
525                Kind::Int => {
526                    self.integer()?;
527                }
528                Kind::Float => {
529                    self.float()?;
530                }
531                Kind::Str => {
532                    self.str()?;
533                }
534                Kind::Bin => {
535                    self.bin()?;
536                }
537                Kind::Ext => {
538                    self.ext()?;
539                }
540                Kind::Array => pending += self.array_len()?,
541                Kind::Map => pending += 2 * self.map_len()?,
542            }
543        }
544        Ok(())
545    }
546
547    fn peek_marker(&self) -> Result<u8> {
548        self.buf().get(self.pos).copied().ok_or(Error::Truncated {
549            offset: self.pos,
550            needed: 1,
551        })
552    }
553
554    /// Consume the marker at `start` and the `N` big-endian bytes after it.
555    fn be<const N: usize>(&mut self, start: usize) -> Result<[u8; N]> {
556        self.pos = start + 1;
557        let bytes = self.take(start, N)?;
558        Ok(bytes.try_into().expect("take returned N bytes"))
559    }
560
561    fn take(&mut self, start: usize, len: usize) -> Result<&'f [u8]> {
562        let buf: &'f [u8] = self.buf();
563        let end = self
564            .pos
565            .checked_add(len)
566            .filter(|end| *end <= buf.len())
567            .ok_or(Error::Truncated {
568                offset: start,
569                needed: (self.pos + len).saturating_sub(buf.len()),
570            })?;
571        let bytes = &buf[self.pos..end];
572        self.pos = end;
573        Ok(bytes)
574    }
575
576    fn take_shared(&mut self, start: usize, len: usize) -> Result<Bytes> {
577        let from = self.pos;
578        self.take(start, len)?;
579        Ok(self.buf().slice(from..self.pos))
580    }
581
582    fn type_error(&self, expected: &'static str) -> Error {
583        let found = self
584            .buf()
585            .get(self.pos)
586            .and_then(|marker| Kind::of(*marker))
587            .map_or("reserved marker", Kind::name);
588        Error::Type {
589            expected,
590            found,
591            offset: self.pos,
592        }
593    }
594}
595
596#[cfg(test)]
597mod tests {
598    use super::*;
599
600    fn encoded(write: impl FnOnce(&mut Encoder)) -> Vec<u8> {
601        let mut encoder = Encoder::new();
602        write(&mut encoder);
603        encoder.into_single_frame()
604    }
605
606    /// The format boundaries msgspec uses, as observed from the Python encoder.
607    #[test]
608    fn integers_use_the_smallest_format() {
609        let cases: &[(i64, &[u8])] = &[
610            (127, &[0x7f]),
611            (128, &[0xcc, 0x80]),
612            (255, &[0xcc, 0xff]),
613            (256, &[0xcd, 0x01, 0x00]),
614            (65535, &[0xcd, 0xff, 0xff]),
615            (65536, &[0xce, 0x00, 0x01, 0x00, 0x00]),
616            (-1, &[0xff]),
617            (-32, &[0xe0]),
618            (-33, &[0xd0, 0xdf]),
619            (-128, &[0xd0, 0x80]),
620            (-129, &[0xd1, 0xff, 0x7f]),
621            (1 << 32, &[0xcf, 0, 0, 0, 1, 0, 0, 0, 0]),
622        ];
623        for (value, expected) in cases {
624            assert_eq!(encoded(|e| e.int(*value)), *expected, "{value}");
625            let frames = [Bytes::copy_from_slice(expected)];
626            let mut decoder = Decoder::new(&frames);
627            let decoded: i64 = decoder.integer().unwrap().narrow("i64").unwrap();
628            assert_eq!(decoded, *value);
629            decoder.finish().unwrap();
630        }
631    }
632
633    #[test]
634    fn strings_switch_to_str8_at_32_bytes() {
635        assert_eq!(encoded(|e| e.str(&"a".repeat(31)))[0], 0xbf);
636        assert_eq!(encoded(|e| e.str(&"a".repeat(32)))[..2], [0xd9, 32]);
637    }
638
639    #[test]
640    fn ext_uses_fixext_for_power_of_two_sizes() {
641        assert_eq!(encoded(|e| e.ext(3, b"abcd")), b"\xd6\x03abcd");
642        assert_eq!(encoded(|e| e.ext(3, b"abc")), b"\xc7\x03\x03abc");
643    }
644
645    #[test]
646    fn float_fields_accept_integers() {
647        let frames = [Bytes::from_static(&[0x03])];
648        assert_eq!(Decoder::new(&frames).float().unwrap(), 3.0);
649    }
650
651    #[test]
652    fn truncated_and_trailing_input_fail() {
653        let frames = [Bytes::from_static(&[0xcd, 0x01])];
654        assert!(matches!(
655            Decoder::new(&frames).integer(),
656            Err(Error::Truncated { .. })
657        ));
658        let frames = [Bytes::from_static(&[0x01, 0x02])];
659        let mut decoder = Decoder::new(&frames);
660        decoder.integer().unwrap();
661        assert!(matches!(decoder.finish(), Err(Error::TrailingBytes(1))));
662    }
663
664    #[test]
665    fn narrowing_overflow_fails() {
666        let frames = [Bytes::from_static(&[0xcf, 0x80, 0, 0, 0, 0, 0, 0, 0])];
667        let value = Decoder::new(&frames).integer().unwrap();
668        assert!(matches!(
669            value.narrow::<i64>("i64"),
670            Err(Error::IntRange { .. })
671        ));
672        assert_eq!(value.narrow::<u64>("u64").unwrap(), 1 << 63);
673    }
674
675    #[test]
676    fn skip_walks_nested_values() {
677        let mut encoder = Encoder::new();
678        encoder.array_len(2);
679        encoder.map_len(1);
680        encoder.str("k");
681        encoder.array_len(2);
682        encoder.float(1.5);
683        encoder.nil();
684        encoder.bin(b"xy");
685        encoder.int(7);
686        let frames = [Bytes::from(encoder.into_single_frame())];
687        let mut decoder = Decoder::new(&frames);
688        decoder.skip().unwrap();
689        assert_eq!(decoder.integer().unwrap(), Integer::Unsigned(7));
690        decoder.finish().unwrap();
691    }
692}