Skip to main content

iris_abi/
message.rs

1//! The records themselves.
2//!
3//! Each record has a fixed-width part and then a variable-length part, in that order, and new
4//! fields go on the end. A reader reads what it knows and stops; the framing tells it where the
5//! record ended, so stopping early is safe. That single rule is what lets a decoder built against
6//! one version of this crate keep working against a host built against a later one.
7//!
8//! The version number on a record is not a "how new is this" counter. It only goes up when a field
9//! is removed or changes meaning, which is a break, and a break is supposed to be loud.
10
11use crate::caps::{Capability, CapabilitySet};
12use crate::error::{Error, Result};
13use crate::record::{Header, Tag};
14use crate::wire::{Reader, Writer};
15
16/// The host introducing itself.
17#[derive(Clone, Copy, PartialEq, Eq, Debug)]
18pub struct Hello {
19    /// The major ABI version the host speaks. A mismatch here is fatal.
20    pub abi_major: u16,
21    /// The minor ABI version the host speaks. A mismatch here is not fatal.
22    pub abi_minor: u16,
23    /// How many bytes of the source the host is willing to keep visible to the guest at once.
24    ///
25    /// Zero means the host will map the whole source and the decoder never has to think about
26    /// windows.
27    pub window_bytes: u64,
28    /// The largest number of rows the host will ask for in one scan request.
29    pub max_batch_rows: u64,
30    /// What the host can do.
31    pub offered: CapabilitySet,
32    /// How many bytes the source has in total, or zero if the host is not saying.
33    ///
34    /// A decoder that has to find its own footer needs this, and a decoder that reads forwards from
35    /// the start does not, which is why zero is allowed rather than being an error.
36    ///
37    /// This field was appended after the ABI shipped, so it is the first real exercise of the
38    /// grow-at-the-end rule. A host built before it existed writes a `Hello` that ends after
39    /// `offered`, and a decoder built after it reads zero and carries on.
40    pub source_bytes: u64,
41}
42
43impl Hello {
44    /// The layout version of this record.
45    pub const VERSION: u16 = 1;
46
47    /// Writes the record.
48    ///
49    /// # Errors
50    ///
51    /// Returns [`Error::BufferFull`] if the buffer runs out.
52    pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
53        w.record(Tag::HELLO, Self::VERSION, |w| {
54            w.u16(self.abi_major)?;
55            w.u16(self.abi_minor)?;
56            w.u32(0)?;
57            w.u64(self.window_bytes)?;
58            w.u64(self.max_batch_rows)?;
59            w.var_bytes(self.offered.as_bytes())?;
60            w.u64(self.source_bytes)
61        })
62    }
63
64    /// Reads the record from its payload.
65    ///
66    /// # Errors
67    ///
68    /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows, or
69    /// [`Error::Truncated`] if the payload ends early.
70    pub fn decode(version: u16, p: &mut Reader<'_>) -> Result<Self> {
71        expect_version(Tag::HELLO, version)?;
72        let abi_major = p.u16()?;
73        let abi_minor = p.u16()?;
74        p.skip(4)?;
75        let window_bytes = p.u64()?;
76        let max_batch_rows = p.u64()?;
77        let offered = p.capability_set()?;
78        let source_bytes = p.opt_u64()?.unwrap_or(0);
79        Ok(Self {
80            abi_major,
81            abi_minor,
82            window_bytes,
83            max_batch_rows,
84            offered,
85            source_bytes,
86        })
87    }
88}
89
90/// The decoder answering the host.
91#[derive(Clone, Copy, PartialEq, Eq, Debug)]
92pub struct HelloAck<'a> {
93    /// The major ABI version the decoder was built against.
94    pub abi_major: u16,
95    /// The minor ABI version the decoder was built against.
96    pub abi_minor: u16,
97    /// What the decoder cannot run without. If the host does not offer all of these, the two sides
98    /// stop here.
99    pub required: CapabilitySet,
100    /// What the decoder will use if it is there and do without if it is not.
101    pub optional: CapabilitySet,
102    /// A name for the decoder, for logs and error messages. Not interpreted.
103    pub decoder_id: &'a str,
104}
105
106impl<'a> HelloAck<'a> {
107    /// The layout version of this record.
108    pub const VERSION: u16 = 1;
109
110    /// Writes the record.
111    ///
112    /// # Errors
113    ///
114    /// Returns [`Error::BufferFull`] if the buffer runs out.
115    pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
116        w.record(Tag::HELLO_ACK, Self::VERSION, |w| {
117            w.u16(self.abi_major)?;
118            w.u16(self.abi_minor)?;
119            w.u32(0)?;
120            w.var_bytes(self.required.as_bytes())?;
121            w.var_bytes(self.optional.as_bytes())?;
122            w.var_str(self.decoder_id)
123        })
124    }
125
126    /// Reads the record from its payload.
127    ///
128    /// # Errors
129    ///
130    /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
131    /// [`Error::Truncated`] if the payload ends early, or [`Error::NotUtf8`] if the decoder name is
132    /// not text.
133    pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
134        expect_version(Tag::HELLO_ACK, version)?;
135        let abi_major = p.u16()?;
136        let abi_minor = p.u16()?;
137        p.skip(4)?;
138        let required = p.capability_set()?;
139        let optional = p.capability_set()?;
140        let decoder_id = p.var_str()?;
141        Ok(Self {
142            abi_major,
143            abi_minor,
144            required,
145            optional,
146            decoder_id,
147        })
148    }
149}
150
151/// Why one side is declining to go on.
152#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
153pub struct RefusalReason(pub u16);
154
155impl RefusalReason {
156    /// The other side needs a capability this side does not have.
157    pub const MISSING_CAPABILITY: Self = Self(1);
158    /// The other side speaks a major ABI version from the future.
159    pub const ABI_TOO_NEW: Self = Self(2);
160    /// The other side speaks a major ABI version that is no longer supported.
161    pub const ABI_TOO_OLD: Self = Self(3);
162    /// A record arrived that this side does not know how to handle and cannot skip.
163    pub const UNSUPPORTED_RECORD: Self = Self(4);
164    /// The bytes did not parse.
165    pub const MALFORMED: Self = Self(5);
166    /// The request is larger than this side is willing to serve.
167    pub const RESOURCE_LIMIT: Self = Self(6);
168    /// This side is able to do what was asked and is choosing not to.
169    pub const POLICY: Self = Self(7);
170
171    /// The name of this reason, if it is one we assigned.
172    #[must_use]
173    pub const fn name(self) -> Option<&'static str> {
174        match self {
175            Self::MISSING_CAPABILITY => Some("missing capability"),
176            Self::ABI_TOO_NEW => Some("ABI version too new"),
177            Self::ABI_TOO_OLD => Some("ABI version too old"),
178            Self::UNSUPPORTED_RECORD => Some("unsupported record"),
179            Self::MALFORMED => Some("malformed record"),
180            Self::RESOURCE_LIMIT => Some("resource limit"),
181            Self::POLICY => Some("refused by policy"),
182            _ => None,
183        }
184    }
185}
186
187impl core::fmt::Display for RefusalReason {
188    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189        match self.name() {
190            Some(name) => f.write_str(name),
191            None => write!(f, "refusal reason {}", self.0),
192        }
193    }
194}
195
196/// One side declining to go on, and saying why.
197///
198/// The reason a refusal is a record rather than a dropped connection is that "this did not work" is
199/// not an actionable message. Somebody has to be able to read the failure and know which capability
200/// to go and implement.
201#[derive(Clone, Copy, PartialEq, Eq, Debug)]
202pub struct Refusal<'a> {
203    /// The category of problem.
204    pub reason: RefusalReason,
205    /// Which capability was missing, when `reason` is [`RefusalReason::MISSING_CAPABILITY`].
206    pub capability: Capability,
207    /// Text for a human. Not parsed by anything.
208    pub detail: &'a str,
209}
210
211impl<'a> Refusal<'a> {
212    /// The layout version of this record.
213    pub const VERSION: u16 = 1;
214
215    /// A refusal with no particular capability attached.
216    #[must_use]
217    pub const fn new(reason: RefusalReason, detail: &'a str) -> Self {
218        Self {
219            reason,
220            capability: Capability(u16::MAX),
221            detail,
222        }
223    }
224
225    /// Writes the record.
226    ///
227    /// # Errors
228    ///
229    /// Returns [`Error::BufferFull`] if the buffer runs out.
230    pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
231        w.record(Tag::REFUSAL, Self::VERSION, |w| {
232            w.u16(self.reason.0)?;
233            w.u16(self.capability.0)?;
234            w.u32(0)?;
235            w.var_str(self.detail)
236        })
237    }
238
239    /// Reads the record from its payload.
240    ///
241    /// # Errors
242    ///
243    /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
244    /// [`Error::Truncated`] if the payload ends early, or [`Error::NotUtf8`] if the detail is not
245    /// text.
246    pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
247        expect_version(Tag::REFUSAL, version)?;
248        let reason = RefusalReason(p.u16()?);
249        let capability = Capability(p.u16()?);
250        p.skip(4)?;
251        let detail = p.var_str()?;
252        Ok(Self {
253            reason,
254            capability,
255            detail,
256        })
257    }
258}
259
260/// The columns a scan is being asked for.
261///
262/// This is a list of indices and not a bitmask. A bitmask has to pick a width, and whatever width
263/// it picks becomes the maximum number of columns the format can ever describe. Wide tables are
264/// exactly where a columnar format is supposed to win, so putting a ceiling on the column count is
265/// the wrong place to save four bytes.
266#[derive(Clone, Copy, PartialEq, Eq, Debug)]
267pub struct Projection<'a> {
268    raw: &'a [u8],
269}
270
271impl<'a> Projection<'a> {
272    /// An empty projection, which means every column.
273    pub const ALL: Self = Self { raw: &[] };
274
275    /// Wraps the encoded form.
276    ///
277    /// # Errors
278    ///
279    /// Returns [`Error::Malformed`] if the length is not a whole number of column indices.
280    pub fn from_bytes(raw: &'a [u8]) -> Result<Self> {
281        if !raw.len().is_multiple_of(4) {
282            return Err(Error::Malformed(
283                "a projection must be a whole number of four byte column indices",
284            ));
285        }
286        Ok(Self { raw })
287    }
288
289    /// How many columns are named.
290    #[must_use]
291    pub const fn len(self) -> usize {
292        self.raw.len() / 4
293    }
294
295    /// Whether the projection names no columns, which means every column.
296    #[must_use]
297    pub const fn is_empty(self) -> bool {
298        self.raw.is_empty()
299    }
300
301    /// The column indices, in the order the caller wrote them.
302    pub fn iter(self) -> impl Iterator<Item = u32> + 'a {
303        self.raw
304            .as_chunks::<4>()
305            .0
306            .iter()
307            .map(|c| u32::from_le_bytes(*c))
308    }
309
310    /// The encoded form.
311    #[must_use]
312    pub const fn as_bytes(self) -> &'a [u8] {
313        self.raw
314    }
315}
316
317/// The host asking the decoder for a run of rows.
318#[derive(Clone, Copy, PartialEq, Eq, Debug)]
319pub struct ScanRequest<'a> {
320    /// The first row wanted, counting from zero.
321    ///
322    /// This is 64 bits because a row count that fits in 32 bits is a limit somebody will hit, and
323    /// the whole point of the exercise is not to build limits into a format that ossifies.
324    pub row_start: u64,
325    /// How many rows are wanted. `u64::MAX` means "everything from `row_start` on".
326    pub row_count: u64,
327    /// Flags, all currently reserved and required to be zero.
328    pub flags: u64,
329    /// Which columns are wanted.
330    pub projection: Projection<'a>,
331    /// A filter for the decoder to apply, in a form the decoder and the host have agreed on
332    /// separately. Empty means no filter.
333    pub filter: &'a [u8],
334}
335
336impl<'a> ScanRequest<'a> {
337    /// The layout version of this record.
338    pub const VERSION: u16 = 1;
339
340    /// A request for every row and every column.
341    #[must_use]
342    pub const fn everything() -> Self {
343        Self {
344            row_start: 0,
345            row_count: u64::MAX,
346            flags: 0,
347            projection: Projection::ALL,
348            filter: &[],
349        }
350    }
351
352    /// Writes the record.
353    ///
354    /// # Errors
355    ///
356    /// Returns [`Error::BufferFull`] if the buffer runs out.
357    pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
358        w.record(Tag::SCAN_REQUEST, Self::VERSION, |w| {
359            w.u64(self.row_start)?;
360            w.u64(self.row_count)?;
361            w.u64(self.flags)?;
362            w.var_bytes(self.projection.as_bytes())?;
363            w.var_bytes(self.filter)
364        })
365    }
366
367    /// Reads the record from its payload.
368    ///
369    /// # Errors
370    ///
371    /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
372    /// [`Error::Truncated`] if the payload ends early, or [`Error::Malformed`] if the projection is
373    /// not a whole number of column indices.
374    pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
375        expect_version(Tag::SCAN_REQUEST, version)?;
376        let row_start = p.u64()?;
377        let row_count = p.u64()?;
378        let flags = p.u64()?;
379        let projection = Projection::from_bytes(p.var_bytes()?)?;
380        let filter = p.var_bytes()?;
381        Ok(Self {
382            row_start,
383            row_count,
384            flags,
385            projection,
386            filter,
387        })
388    }
389}
390
391/// The decoder asking for bytes of the source.
392///
393/// This is the record the whole design turns on. The decoder says which bytes it needs and the host
394/// decides how to get them, which is what keeps file handles, caching, prefetching, object store
395/// credentials and retry policy on the host side of the boundary where they can be fixed without
396/// recompiling anybody's decoder.
397#[derive(Clone, Copy, PartialEq, Eq, Debug)]
398pub struct RangeRequest {
399    /// Where the wanted bytes start in the source.
400    pub offset: u64,
401    /// How many bytes are wanted.
402    pub len: u64,
403}
404
405impl RangeRequest {
406    /// The layout version of this record.
407    pub const VERSION: u16 = 1;
408
409    /// Writes the record.
410    ///
411    /// # Errors
412    ///
413    /// Returns [`Error::BufferFull`] if the buffer runs out.
414    pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
415        w.record(Tag::RANGE_REQUEST, Self::VERSION, |w| {
416            w.u64(self.offset)?;
417            w.u64(self.len)
418        })
419    }
420
421    /// Reads the record from its payload.
422    ///
423    /// # Errors
424    ///
425    /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows, or
426    /// [`Error::Truncated`] if the payload ends early.
427    pub fn decode(version: u16, p: &mut Reader<'_>) -> Result<Self> {
428        expect_version(Tag::RANGE_REQUEST, version)?;
429        let offset = p.u64()?;
430        let len = p.u64()?;
431        Ok(Self { offset, len })
432    }
433}
434
435/// One array in a batch, in the flattened order the schema puts them in.
436///
437/// This is the same pair Arrow IPC calls a field node, and it is here for the same reason: a
438/// column's length and null count are not derivable from its buffers, so somebody has to say them
439/// out loud.
440#[derive(Clone, Copy, PartialEq, Eq, Debug)]
441pub struct Node {
442    /// How many slots the array has.
443    pub length: u64,
444    /// How many of those slots are null.
445    pub null_count: u64,
446}
447
448impl Node {
449    /// How wide the encoded form is, in bytes.
450    pub const SIZE: usize = 16;
451}
452
453/// Where one Arrow buffer sits in the decoder's memory.
454///
455/// Offsets are 64 bits wide even though a `wasm32` guest cannot address more than four gigabytes,
456/// because the width of a guest address is not something worth writing into a format that ossifies.
457#[derive(Clone, Copy, PartialEq, Eq, Debug)]
458pub struct BufferRef {
459    /// Where the buffer starts in the decoder's memory.
460    pub offset: u64,
461    /// How many bytes long it is.
462    pub len: u64,
463}
464
465impl BufferRef {
466    /// How wide the encoded form is, in bytes.
467    pub const SIZE: usize = 16;
468
469    /// One past the last byte, or `None` if the two fields overflow.
470    #[must_use]
471    pub const fn end(&self) -> Option<u64> {
472        self.offset.checked_add(self.len)
473    }
474}
475
476/// A list of [`Node`]s, still in its encoded form.
477#[derive(Clone, Copy, PartialEq, Eq, Debug)]
478pub struct Nodes<'a> {
479    raw: &'a [u8],
480}
481
482impl<'a> Nodes<'a> {
483    /// No arrays at all.
484    pub const EMPTY: Self = Self { raw: &[] };
485
486    /// Wraps the encoded form.
487    ///
488    /// # Errors
489    ///
490    /// Returns [`Error::Malformed`] if the length is not a whole number of nodes.
491    pub fn from_bytes(raw: &'a [u8]) -> Result<Self> {
492        if !raw.len().is_multiple_of(Node::SIZE) {
493            return Err(Error::Malformed(
494                "a node list must be a whole number of sixteen byte nodes",
495            ));
496        }
497        Ok(Self { raw })
498    }
499
500    /// How many arrays are described.
501    #[must_use]
502    pub const fn len(self) -> usize {
503        self.raw.len() / Node::SIZE
504    }
505
506    /// Whether no arrays are described.
507    #[must_use]
508    pub const fn is_empty(self) -> bool {
509        self.raw.is_empty()
510    }
511
512    /// The nodes, in order.
513    pub fn iter(self) -> impl Iterator<Item = Node> + 'a {
514        self.raw.as_chunks::<{ Node::SIZE }>().0.iter().map(|c| {
515            let (length, null_count) = c.split_at(8);
516            Node {
517                length: u64::from_le_bytes(length.try_into().unwrap_or([0; 8])),
518                null_count: u64::from_le_bytes(null_count.try_into().unwrap_or([0; 8])),
519            }
520        })
521    }
522
523    /// The encoded form.
524    #[must_use]
525    pub const fn as_bytes(self) -> &'a [u8] {
526        self.raw
527    }
528}
529
530/// A list of [`BufferRef`]s, still in its encoded form.
531#[derive(Clone, Copy, PartialEq, Eq, Debug)]
532pub struct Buffers<'a> {
533    raw: &'a [u8],
534}
535
536impl<'a> Buffers<'a> {
537    /// No buffers at all.
538    pub const EMPTY: Self = Self { raw: &[] };
539
540    /// Wraps the encoded form.
541    ///
542    /// # Errors
543    ///
544    /// Returns [`Error::Malformed`] if the length is not a whole number of buffer references.
545    pub fn from_bytes(raw: &'a [u8]) -> Result<Self> {
546        if !raw.len().is_multiple_of(BufferRef::SIZE) {
547            return Err(Error::Malformed(
548                "a buffer list must be a whole number of sixteen byte references",
549            ));
550        }
551        Ok(Self { raw })
552    }
553
554    /// How many buffers are described.
555    #[must_use]
556    pub const fn len(self) -> usize {
557        self.raw.len() / BufferRef::SIZE
558    }
559
560    /// Whether no buffers are described.
561    #[must_use]
562    pub const fn is_empty(self) -> bool {
563        self.raw.is_empty()
564    }
565
566    /// The buffer references, in order.
567    pub fn iter(self) -> impl Iterator<Item = BufferRef> + 'a {
568        self.raw
569            .as_chunks::<{ BufferRef::SIZE }>()
570            .0
571            .iter()
572            .map(|c| {
573                let (offset, len) = c.split_at(8);
574                BufferRef {
575                    offset: u64::from_le_bytes(offset.try_into().unwrap_or([0; 8])),
576                    len: u64::from_le_bytes(len.try_into().unwrap_or([0; 8])),
577                }
578            })
579    }
580
581    /// The encoded form.
582    #[must_use]
583    pub const fn as_bytes(self) -> &'a [u8] {
584        self.raw
585    }
586}
587
588/// The decoder handing back one batch of decoded rows.
589///
590/// A batch says how many rows it has and then describes the Arrow arrays behind them as a flat list
591/// of nodes and a flat list of buffers, both in the pre-order the schema puts its fields in. That is
592/// the same shape Arrow IPC uses, and it is the right one here for the same reason: the host already
593/// has the schema, so the schema decides how many nodes and buffers there should be and the batch
594/// only has to supply them.
595///
596/// Neither list carries a count. The record length bounds both of them, so a batch cannot claim a
597/// million columns without being large enough to describe a million columns. That is the same rule
598/// the container format follows, and it is the difference between allocation safety being structural
599/// and allocation safety being something a reviewer has to remember.
600///
601/// The buffers are not in this record. They are in the decoder's memory, and the offsets say where.
602/// Whether those offsets are inside the decoder's memory, and whether the bytes at them are a valid
603/// Arrow array, are two separate questions and neither of them is answered here.
604#[derive(Clone, Copy, PartialEq, Eq, Debug)]
605pub struct Batch<'a> {
606    /// How many rows the batch has.
607    pub rows: u64,
608    /// Flags, all currently reserved and required to be zero.
609    pub flags: u64,
610    /// One node per array, in schema pre-order.
611    pub nodes: Nodes<'a>,
612    /// One reference per Arrow buffer, in schema pre-order.
613    pub buffers: Buffers<'a>,
614}
615
616impl<'a> Batch<'a> {
617    /// The layout version of this record.
618    pub const VERSION: u16 = 1;
619
620    /// An empty batch, which is how a decoder says a scan produced no more rows.
621    #[must_use]
622    pub const fn empty() -> Self {
623        Self {
624            rows: 0,
625            flags: 0,
626            nodes: Nodes::EMPTY,
627            buffers: Buffers::EMPTY,
628        }
629    }
630
631    /// Writes the record.
632    ///
633    /// # Errors
634    ///
635    /// Returns [`Error::BufferFull`] if the buffer runs out.
636    pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
637        w.record(Tag::BATCH, Self::VERSION, |w| {
638            w.u64(self.rows)?;
639            w.u64(self.flags)?;
640            w.var_bytes(self.nodes.as_bytes())?;
641            w.var_bytes(self.buffers.as_bytes())
642        })
643    }
644
645    /// Reads the record from its payload.
646    ///
647    /// # Errors
648    ///
649    /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
650    /// [`Error::Truncated`] if the payload ends early, or [`Error::Malformed`] if either list is not
651    /// a whole number of entries.
652    pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
653        expect_version(Tag::BATCH, version)?;
654        let rows = p.u64()?;
655        let flags = p.u64()?;
656        let nodes = Nodes::from_bytes(p.var_bytes()?)?;
657        let buffers = Buffers::from_bytes(p.var_bytes()?)?;
658        Ok(Self {
659            rows,
660            flags,
661            nodes,
662            buffers,
663        })
664    }
665}
666
667/// One record, decoded.
668#[derive(Clone, Copy, PartialEq, Eq, Debug)]
669#[non_exhaustive]
670pub enum Message<'a> {
671    /// See [`Hello`].
672    Hello(Hello),
673    /// See [`HelloAck`].
674    HelloAck(HelloAck<'a>),
675    /// See [`Refusal`].
676    Refusal(Refusal<'a>),
677    /// See [`ScanRequest`].
678    ScanRequest(ScanRequest<'a>),
679    /// See [`RangeRequest`].
680    RangeRequest(RangeRequest),
681    /// See [`Batch`].
682    Batch(Batch<'a>),
683    /// A record this build has no code for. It has already been stepped over, so a reader that gets
684    /// one of these can carry on to the next record.
685    Unknown(
686        /// What was on the front of the record that could not be handled.
687        Header,
688    ),
689}
690
691impl<'a> Reader<'a> {
692    /// Reads the next record and decodes it.
693    ///
694    /// An unrecognised tag comes back as [`Message::Unknown`] rather than an error, and the reader
695    /// is left pointing at the record after it. That is the extension point: adding a record to the
696    /// ABI does not break anything that was compiled before it existed.
697    ///
698    /// # Errors
699    ///
700    /// Returns [`Error::Truncated`] if the buffer ends inside the record, or whatever the
701    /// individual record's decoder returns.
702    pub fn message(&mut self) -> Result<Message<'a>> {
703        let (header, mut p) = self.record()?;
704        let v = header.version;
705        Ok(match header.tag {
706            Tag::HELLO => Message::Hello(Hello::decode(v, &mut p)?),
707            Tag::HELLO_ACK => Message::HelloAck(HelloAck::decode(v, &mut p)?),
708            Tag::REFUSAL => Message::Refusal(Refusal::decode(v, &mut p)?),
709            Tag::SCAN_REQUEST => Message::ScanRequest(ScanRequest::decode(v, &mut p)?),
710            Tag::RANGE_REQUEST => Message::RangeRequest(RangeRequest::decode(v, &mut p)?),
711            Tag::BATCH => Message::Batch(Batch::decode(v, &mut p)?),
712            _ => Message::Unknown(header),
713        })
714    }
715}
716
717fn expect_version(tag: Tag, version: u16) -> Result<()> {
718    // Adding a field to the end of a record does not change this number, because a reader that does
719    // not know about the field skips it and is still correct. The number only moves when a field is
720    // removed or changes meaning, and at that point a reader that guesses is worse than one that
721    // stops. A future version 2 of a record keeps a branch here for version 1.
722    let known = match tag {
723        Tag::HELLO => Hello::VERSION,
724        Tag::HELLO_ACK => HelloAck::VERSION,
725        Tag::REFUSAL => Refusal::VERSION,
726        Tag::SCAN_REQUEST => ScanRequest::VERSION,
727        Tag::RANGE_REQUEST => RangeRequest::VERSION,
728        Tag::BATCH => Batch::VERSION,
729        _ => return Err(Error::Malformed("no version is defined for this tag")),
730    };
731    if version == known {
732        Ok(())
733    } else {
734        Err(Error::UnsupportedVersion { tag, version })
735    }
736}