wasmbin/
sections.rs

1//! [Module sections](https://webassembly.github.io/spec/core/binary/modules.html#sections).
2
3// Copyright 2020 Google Inc. All Rights Reserved.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use crate::builtins::{Blob, Lazy, UnparsedBytes, WasmbinCountable};
18#[cfg(feature = "exception-handling")]
19use crate::indices::ExceptionId;
20#[cfg(feature = "extended-name-section")]
21use crate::indices::{DataId, ElemId, LabelId};
22use crate::indices::{FuncId, GlobalId, LocalId, MemId, TableId, TypeId};
23use crate::instructions::Expression;
24use crate::io::{Decode, DecodeError, DecodeWithDiscriminant, Encode, PathItem, Wasmbin};
25#[cfg(feature = "exception-handling")]
26use crate::types::ExceptionType;
27use crate::types::{FuncType, GlobalType, MemType, RefType, TableType, ValueType};
28use crate::visit::{Visit, VisitError};
29use custom_debug::Debug as CustomDebug;
30use std::convert::TryFrom;
31use thiserror::Error;
32
33/// A [name association](https://webassembly.github.io/spec/core/appendix/custom.html#binary-namemap) key-value pair.
34///
35/// Might also be used to represent an [indirect name association](https://webassembly.github.io/spec/core/appendix/custom.html#binary-indirectnamemap).
36#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
37pub struct NameAssoc<I, V = String> {
38    pub index: I,
39    pub value: V,
40}
41
42impl<I, V> WasmbinCountable for NameAssoc<I, V> {}
43
44/// [Name map](https://webassembly.github.io/spec/core/appendix/custom.html#binary-namemap).
45#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
46pub struct NameMap<I, V = String> {
47    pub items: Vec<NameAssoc<I, V>>,
48}
49
50/// [Indirect name map](https://webassembly.github.io/spec/core/appendix/custom.html#binary-indirectnamemap).
51pub type IndirectNameMap<I1, I2> = NameMap<I1, NameMap<I2>>;
52
53/// [Name subsection](https://webassembly.github.io/spec/core/appendix/custom.html#subsections).
54#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
55#[repr(u8)]
56pub enum NameSubSection {
57    /// [Module name](https://webassembly.github.io/spec/core/appendix/custom.html#module-names).
58    Module(Blob<String>) = 0,
59    /// [Function names](https://webassembly.github.io/spec/core/appendix/custom.html#function-names).
60    Func(Blob<NameMap<FuncId>>) = 1,
61    /// [Local names](https://webassembly.github.io/spec/core/appendix/custom.html#local-names) grouped by function index.
62    Local(Blob<IndirectNameMap<FuncId, LocalId>>) = 2,
63    #[cfg(feature = "extended-name-section")]
64    /// [Label names](https://www.scheidecker.net/2019-07-08-extended-name-section-spec/appendix/custom.html#label-names) grouped by function index.
65    Label(Blob<IndirectNameMap<FuncId, LabelId>>) = 3,
66    #[cfg(feature = "extended-name-section")]
67    /// [Type names](https://www.scheidecker.net/2019-07-08-extended-name-section-spec/appendix/custom.html#type-names).
68    Type(Blob<NameMap<TypeId>>) = 4,
69    #[cfg(feature = "extended-name-section")]
70    /// [Table names](https://www.scheidecker.net/2019-07-08-extended-name-section-spec/appendix/custom.html#table-names).
71    Table(Blob<NameMap<TableId>>) = 5,
72    #[cfg(feature = "extended-name-section")]
73    /// [Memory names](https://www.scheidecker.net/2019-07-08-extended-name-section-spec/appendix/custom.html#memory-names).
74    Memory(Blob<NameMap<MemId>>) = 6,
75    #[cfg(feature = "extended-name-section")]
76    /// [Global names](https://www.scheidecker.net/2019-07-08-extended-name-section-spec/appendix/custom.html#global-names).
77    Global(Blob<NameMap<GlobalId>>) = 7,
78    #[cfg(feature = "extended-name-section")]
79    /// Element segment names.
80    Elem(Blob<NameMap<ElemId>>) = 8,
81    #[cfg(feature = "extended-name-section")]
82    /// Data segment names.
83    Data(Blob<NameMap<DataId>>) = 9,
84}
85
86impl Encode for [NameSubSection] {
87    fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
88        for sub in self {
89            sub.encode(w)?;
90        }
91        Ok(())
92    }
93}
94
95impl Decode for Vec<NameSubSection> {
96    fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
97        let mut sub = Vec::new();
98        while let Some(disc) = Option::decode(r)? {
99            let i = sub.len();
100            sub.push(
101                NameSubSection::decode_with_discriminant(disc, r)
102                    .map_err(move |err| err.in_path(PathItem::Index(i)))?,
103            );
104        }
105        Ok(sub)
106    }
107}
108
109/// [`producer`](https://github.com/WebAssembly/tool-conventions/blob/08bacbed7d0daff49808370cd93b6a6f0c962d76/ProducersSection.md#custom-section) field.
110#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
111pub struct ProducerField {
112    pub name: String,
113    pub values: Vec<ProducerVersionedName>,
114}
115
116/// [`producer`](https://github.com/WebAssembly/tool-conventions/blob/08bacbed7d0daff49808370cd93b6a6f0c962d76/ProducersSection.md#custom-section) `versioned-name` structure.
117#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
118pub struct ProducerVersionedName {
119    pub name: String,
120    pub version: String,
121}
122
123/// A raw [custom section](https://webassembly.github.io/spec/core/binary/modules.html#custom-section).
124///
125/// Used to represent custom sections with unknown semantics.
126#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
127pub struct RawCustomSection {
128    pub name: String,
129    pub data: UnparsedBytes,
130}
131
132macro_rules! define_custom_sections {
133    ($(#[doc = $url:literal] $name:ident($ty:ty) = $disc:literal,)*) => {
134        /// A [custom section](https://webassembly.github.io/spec/core/binary/modules.html#custom-section).
135        ///
136        /// This enum supports some non-standard custom sections commonly used in tooling, but is marked
137        /// as non-exhaustive to allow for future additions that would transform some sections
138        /// currently represented by the [`Other`](CustomSection::Other) variant into new variants.
139        #[derive(Debug, PartialEq, Eq, Hash, Clone)]
140        #[non_exhaustive]
141        pub enum CustomSection {
142            $(
143                #[doc = "[`"]
144                #[doc = $disc]
145                #[doc = "`]("]
146                #[doc = $url]
147                #[doc = ") custom section."]
148                $name($ty),
149            )*
150            /// A custom section that is not recognized by this library.
151            Other(RawCustomSection),
152        }
153
154        impl CustomSection {
155            /// Name of this custom section.
156            pub fn name(&self) -> &str {
157                match self {
158                    $(Self::$name(_) => $disc,)*
159                    Self::Other(raw) => raw.name.as_str(),
160                }
161            }
162        }
163
164        impl Encode for CustomSection {
165            fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
166                match self {
167                    $(CustomSection::$name(data) => {
168                        $disc.encode(w)?;
169                        data.encode(w)
170                    })*
171                    CustomSection::Other(raw) => raw.encode(w)
172                }
173            }
174        }
175
176        impl Decode for CustomSection {
177            fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
178                let name = String::decode(r)?;
179                Ok(match name.as_str() {
180                    $($disc => CustomSection::$name(<$ty>::decode(r)?),)*
181                    _ => CustomSection::Other(RawCustomSection {
182                        name,
183                        data: UnparsedBytes::decode(r)?
184                    })
185                })
186            }
187        }
188
189        impl Visit for CustomSection {
190            fn visit_children<'a, VisitT: 'static, E, F: FnMut(&'a VisitT) -> Result<(), E>>(
191                &'a self,
192                f: &mut F,
193            ) -> Result<(), VisitError<E>> {
194                // Custom section decoding errors must be ignored.
195                drop(match self {
196                    $(CustomSection::$name(data) => Visit::visit_child(data, f),)*
197                    CustomSection::Other(raw) => Visit::visit_child(raw, f),
198                });
199                Ok(())
200            }
201
202            fn visit_children_mut<VisitT: 'static, E, F: FnMut(&mut VisitT) -> Result<(), E>>(
203                &mut self,
204                f: &mut F,
205            ) -> Result<(), VisitError<E>> {
206                // Custom section decoding errors must be ignored.
207                drop(match self {
208                    $(CustomSection::$name(data) => Visit::visit_child_mut(data, f),)*
209                    CustomSection::Other(raw) => Visit::visit_child_mut(raw, f),
210                });
211                Ok(())
212            }
213        }
214    };
215}
216
217define_custom_sections! {
218    /// https://webassembly.github.io/spec/core/appendix/custom.html#name-section
219    Name(Lazy<Vec<NameSubSection>>) = "name",
220    /// https://github.com/WebAssembly/tool-conventions/blob/08bacbed7d0daff49808370cd93b6a6f0c962d76/ProducersSection.md
221    Producers(Lazy<Vec<ProducerField>>) = "producers",
222    /// https://github.com/WebAssembly/tool-conventions/blob/08bacbed/Debugging.md#external-dwarf
223    ExternalDebugInfo(Lazy<String>) = "external_debug_info",
224    /// https://github.com/WebAssembly/tool-conventions/blob/08bacbed/Debugging.md#source-maps
225    SourceMappingUrl(Lazy<String>) = "sourceMappingURL",
226    /// https://github.com/WebAssembly/tool-conventions/blob/9b80cd2339c648822bb845a083d9ffa6e20fb1ee/BuildId.md
227    BuildId(Vec<u8>) = "build_id",
228}
229
230/// [Import descriptor](https://webassembly.github.io/spec/core/binary/modules.html#binary-importdesc).
231#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
232#[repr(u8)]
233pub enum ImportDesc {
234    Func(TypeId) = 0x00,
235    Table(TableType) = 0x01,
236    Mem(MemType) = 0x02,
237    Global(GlobalType) = 0x03,
238    #[cfg(feature = "exception-handling")]
239    Exception(ExceptionType) = 0x04,
240}
241
242/// [Import](https://webassembly.github.io/spec/core/binary/modules.html#import-section) path.
243#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
244pub struct ImportPath {
245    pub module: String,
246    pub name: String,
247}
248
249/// A single [import](https://webassembly.github.io/spec/core/binary/modules.html#binary-import).
250#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
251pub struct Import {
252    pub path: ImportPath,
253    pub desc: ImportDesc,
254}
255
256/// A single [global](https://webassembly.github.io/spec/core/binary/modules.html#binary-global).
257#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
258pub struct Global {
259    pub ty: GlobalType,
260    pub init: Expression,
261}
262
263/// [Export descriptor](https://webassembly.github.io/spec/core/binary/modules.html#binary-exportdesc).
264#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
265#[repr(u8)]
266pub enum ExportDesc {
267    Func(FuncId) = 0x00,
268    Table(TableId) = 0x01,
269    Mem(MemId) = 0x02,
270    Global(GlobalId) = 0x03,
271    #[cfg(feature = "exception-handling")]
272    Exception(ExceptionId) = 0x04,
273}
274
275/// A single [export](https://webassembly.github.io/spec/core/binary/modules.html#binary-export).
276#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
277pub struct Export {
278    pub name: String,
279    pub desc: ExportDesc,
280}
281
282/// [Element kind](https://webassembly.github.io/spec/core/binary/modules.html#binary-elemkind).
283#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
284#[repr(u8)]
285pub enum ElemKind {
286    FuncRef = 0x00,
287}
288
289/// A single [element](https://webassembly.github.io/spec/core/binary/modules.html#binary-elem).
290#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
291#[repr(u32)]
292pub enum Element {
293    ActiveWithFuncs {
294        offset: Expression,
295        funcs: Vec<FuncId>,
296    } = 0,
297    PassiveWithFuncs {
298        kind: ElemKind,
299        funcs: Vec<FuncId>,
300    } = 1,
301    ActiveWithTableAndFuncs {
302        table: TableId,
303        offset: Expression,
304        kind: ElemKind,
305        funcs: Vec<FuncId>,
306    } = 2,
307    DeclarativeWithFuncs {
308        kind: ElemKind,
309        funcs: Vec<FuncId>,
310    } = 3,
311    ActiveWithExprs {
312        offset: Expression,
313        exprs: Vec<Expression>,
314    } = 4,
315    PassiveWithExprs {
316        ty: RefType,
317        exprs: Vec<Expression>,
318    } = 5,
319    ActiveWithTableAndExprs {
320        table: TableId,
321        offset: Expression,
322        ty: RefType,
323        exprs: Vec<Expression>,
324    } = 6,
325    DeclarativeWithExprs {
326        ty: RefType,
327        exprs: Vec<Expression>,
328    } = 7,
329}
330
331/// Number of repeated consecutive [locals](https://webassembly.github.io/spec/core/binary/modules.html#binary-local) of a single type.
332#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
333pub struct Locals {
334    pub repeat: u32,
335    pub ty: ValueType,
336}
337
338/// [Exception tag](https://webassembly.github.io/exception-handling/core/binary/modules.html#exception-section).
339#[cfg(feature = "exception-handling")]
340#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
341#[wasmbin(discriminant = 0x00)]
342pub struct Exception {
343    pub ty: TypeId,
344}
345
346/// [Function body](https://webassembly.github.io/spec/core/binary/modules.html#binary-func).
347#[derive(Wasmbin, WasmbinCountable, Debug, Default, PartialEq, Eq, Hash, Clone, Visit)]
348pub struct FuncBody {
349    pub locals: Vec<Locals>,
350    pub expr: Expression,
351}
352
353/// [`Data`] segment initialization.
354#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
355#[repr(u32)]
356pub enum DataInit {
357    Active { offset: Expression } = 0,
358    Passive = 1,
359    ActiveWithMemory { memory: MemId, offset: Expression } = 2,
360}
361
362/// [Data segment](https://webassembly.github.io/spec/core/binary/modules.html#binary-data).
363#[derive(Wasmbin, WasmbinCountable, CustomDebug, PartialEq, Eq, Hash, Clone, Visit)]
364pub struct Data {
365    pub init: DataInit,
366    #[debug(with = "custom_debug::hexbuf_str")]
367    pub blob: Vec<u8>,
368}
369
370mod sealed {
371    use super::{Blob, Decode, Encode, Kind, Section};
372
373    pub trait Payload: Encode + Decode + Into<Section> {
374        const KIND: Kind;
375
376        fn try_from_ref(section: &Section) -> Option<&Blob<Self>>;
377        fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>>;
378        fn try_from(section: Section) -> Result<Blob<Self>, Section>;
379    }
380}
381use sealed::Payload;
382
383/// A common marker trait for the [standard payloads](payload).
384pub trait StdPayload: Payload {}
385
386macro_rules! define_sections {
387    ($($(# $attr:tt)* $name:ident($(# $ty_attr:tt)* $ty:ty) = $disc:literal,)*) => {
388        /// Payload types of the [`Section`] variants.
389        pub mod payload {
390            $($(# $attr)* pub type $name = $ty;)*
391        }
392
393        /// [Module section](https://webassembly.github.io/spec/core/binary/modules.html#sections).
394        #[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
395        #[repr(u8)]
396        pub enum Section {
397            $($(# $attr)* $name($(# $ty_attr)* Blob<payload::$name>) = $disc,)*
398        }
399
400        /// A kind of the [`Section`] without the payload.
401        #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
402        #[repr(u8)]
403        pub enum Kind {
404            $($(# $attr)* $name = $disc,)*
405        }
406
407        impl TryFrom<u8> for Kind {
408            type Error = u8;
409
410            fn try_from(discriminant: u8) -> Result<Kind, u8> {
411                #[allow(unused_doc_comments)]
412                Ok(match discriminant {
413                    $($(# $attr)* $disc => Kind::$name,)*
414                    _ => return Err(discriminant),
415                })
416            }
417        }
418
419        impl Ord for Kind {
420            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
421                // Some new sections might have larger discriminants,
422                // but be ordered logically between those will smaller
423                // discriminants.
424                //
425                // To compare their Kinds in a defined order, we need an
426                // intermediate enum without discriminants.
427                #[derive(PartialEq, Eq, PartialOrd, Ord)]
428                #[repr(u8)]
429                enum OrderedRepr {
430                    $($(# $attr)* $name,)*
431                }
432
433                impl From<Kind> for OrderedRepr {
434                    fn from(kind: Kind) -> Self {
435                        #[allow(unused_doc_comments)]
436                        match kind {
437                            $($(# $attr)* Kind::$name => Self::$name,)*
438                        }
439                    }
440                }
441
442                OrderedRepr::from(*self).cmp(&OrderedRepr::from(*other))
443            }
444        }
445
446        impl PartialOrd for Kind {
447            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
448                Some(self.cmp(other))
449            }
450        }
451
452        $($(# $attr)* const _: () = {
453            impl From<Blob<payload::$name>> for Section {
454                fn from(value: Blob<payload::$name>) -> Self {
455                    Section::$name(value)
456                }
457            }
458
459            impl From<payload::$name> for Section {
460                fn from(value: payload::$name) -> Self {
461                    Section::$name(Blob::from(value))
462                }
463            }
464
465            impl Payload for payload::$name {
466                const KIND: Kind = Kind::$name;
467
468                fn try_from_ref(section: &Section) -> Option<&Blob<Self>> {
469                    match section {
470                        Section::$name(res) => Some(res),
471                        _ => None,
472                    }
473                }
474
475                fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>> {
476                    match section {
477                        Section::$name(res) => Some(res),
478                        _ => None,
479                    }
480                }
481
482                fn try_from(section: Section) -> Result<Blob<Self>, Section> {
483                    match section {
484                        Section::$name(res) => Ok(res),
485                        _ => Err(section),
486                    }
487                }
488            }
489        };)*
490
491        impl Section {
492            /// Get the kind of the section without its payload.
493            pub fn kind(&self) -> Kind {
494                #[allow(unused_doc_comments)]
495                match self {
496                    $($(# $attr)* Section::$name(_) => Kind::$name,)*
497                }
498            }
499
500            /// Try to interpret the section as a specific payload.
501            pub fn try_as<T: Payload>(&self) -> Option<&Blob<T>> {
502                T::try_from_ref(self)
503            }
504
505            /// Try to interpret the section as a specific payload mutably.
506            pub fn try_as_mut<T: Payload>(&mut self) -> Option<&mut Blob<T>> {
507                T::try_from_mut(self)
508            }
509        }
510
511        define_sections!(@std $($(# $attr)* $name)*);
512    };
513
514    (@std $(# $ignore_custom_attr:tt)* $ignore_custom:ident $($(# $attr:tt)* $name:ident)*) => {
515        $($(# $attr)* impl StdPayload for payload::$name {})*
516    };
517}
518
519define_sections! {
520    /// [Custom section](https://webassembly.github.io/spec/core/binary/modules.html#custom-section).
521    Custom(super::CustomSection) = 0,
522    /// [Type section](https://webassembly.github.io/spec/core/binary/modules.html#type-section).
523    Type(Vec<super::FuncType>) = 1,
524    /// [Import section](https://webassembly.github.io/spec/core/binary/modules.html#import-section).
525    Import(Vec<super::Import>) = 2,
526    /// [Function section](https://webassembly.github.io/spec/core/binary/modules.html#function-section).
527    Function(Vec<super::TypeId>) = 3,
528    /// [Table section](https://webassembly.github.io/spec/core/binary/modules.html#table-section).
529    Table(Vec<super::TableType>) = 4,
530    /// [Memory section](https://webassembly.github.io/spec/core/binary/modules.html#memory-section).
531    Memory(Vec<super::MemType>) = 5,
532    #[cfg(feature = "exception-handling")]
533    /// [Exception tag section](https://webassembly.github.io/exception-handling/core/binary/modules.html#tag-section).
534    Exception(Vec<super::Exception>) = 13,
535    /// [Global section](https://webassembly.github.io/spec/core/binary/modules.html#global-section).
536    Global(Vec<super::Global>) = 6,
537    /// [Export section](https://webassembly.github.io/spec/core/binary/modules.html#export-section).
538    Export(Vec<super::Export>) = 7,
539    /// [Start section](https://webassembly.github.io/spec/core/binary/modules.html#start-section).
540    Start(
541        /// [Start function](https://webassembly.github.io/spec/core/syntax/modules.html#syntax-start).
542        super::FuncId
543    ) = 8,
544    /// [Element section](https://webassembly.github.io/spec/core/binary/modules.html#element-section).
545    Element(Vec<super::Element>) = 9,
546    /// [Data count section](https://webassembly.github.io/spec/core/binary/modules.html#binary-datacountsec).
547    DataCount(
548        /// Number of data segments in the [`Data`](Section::Data) section.
549        u32
550    ) = 12,
551    /// [Code section](https://webassembly.github.io/spec/core/binary/modules.html#code-section).
552    Code(Vec<super::Blob<super::FuncBody>>) = 10,
553    /// [Data section](https://webassembly.github.io/spec/core/binary/modules.html#data-section).
554    Data(Vec<super::Data>) = 11,
555}
556
557/// Error returned when a section is out of order.
558#[derive(Debug, Error)]
559#[error("Section out of order: {current:?} after {prev:?}")]
560pub struct SectionOrderError {
561    pub current: Kind,
562    pub prev: Kind,
563}
564
565impl From<SectionOrderError> for std::io::Error {
566    fn from(err: SectionOrderError) -> Self {
567        Self::new(std::io::ErrorKind::InvalidData, err)
568    }
569}
570
571struct SectionOrderTracker {
572    last_kind: Kind,
573}
574
575impl Default for SectionOrderTracker {
576    fn default() -> Self {
577        Self {
578            last_kind: Kind::Custom,
579        }
580    }
581}
582
583impl SectionOrderTracker {
584    fn try_add(&mut self, section: &Section) -> Result<(), SectionOrderError> {
585        match section.kind() {
586            Kind::Custom => {}
587            kind if kind > self.last_kind => {
588                self.last_kind = kind;
589            }
590            kind => {
591                return Err(SectionOrderError {
592                    prev: self.last_kind,
593                    current: kind,
594                });
595            }
596        }
597        Ok(())
598    }
599}
600
601impl Encode for [Section] {
602    fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
603        let mut section_order_tracker = SectionOrderTracker::default();
604        for section in self {
605            section_order_tracker.try_add(section)?;
606            section.encode(w)?;
607        }
608        Ok(())
609    }
610}
611
612impl Decode for Vec<Section> {
613    fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
614        let mut sections = Vec::new();
615        let mut section_order_tracker = SectionOrderTracker::default();
616        while let Some(disc) = Option::decode(r)? {
617            let i = sections.len();
618            (|| -> Result<(), DecodeError> {
619                let section = Section::decode_with_discriminant(disc, r)?;
620                section_order_tracker.try_add(&section)?;
621                sections.push(section);
622                Ok(())
623            })()
624            .map_err(move |err| err.in_path(PathItem::Index(i)))?;
625        }
626        Ok(sections)
627    }
628}