beam_file/
chunk.rs

1//! The `Chunk` trait and implementations of commonly used chunks.
2//!
3//! # Reference
4//! - [BEAM File Format][BEAM]
5//! - [`beam_lib`](http://erlang.org/doc/man/beam_lib.html)
6//!
7//! [BEAM]: http://rnyingma.synrc.com/publications/cat/Functional%20Languages/Erlang/BEAM.pdf
8use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
9use libflate::zlib;
10use std::io::{Cursor, Read, Write};
11use std::str;
12
13use crate::parts;
14use crate::Result;
15
16/// The identifier which indicates the type of a chunk.
17pub type Id = [u8; 4];
18
19/// The `Chunk` trait represents a type of chunk in a BEAM file.
20pub trait Chunk {
21    /// Returns the identifier of the chunk.
22    fn id(&self) -> &Id;
23
24    /// Reads a chunk from `reader`.
25    fn decode<R: Read>(mut reader: R) -> Result<Self>
26    where
27        Self: Sized,
28    {
29        let header = aux::Header::decode(&mut reader)?;
30        let mut buf = vec![0; header.data_size as usize];
31        reader.read_exact(&mut buf)?;
32        for _ in 0..aux::padding_size(header.data_size) {
33            reader.read_u8()?;
34        }
35
36        Self::decode_data(&header.chunk_id, Cursor::new(&buf))
37    }
38
39    /// Reads a chunk which has the identifier `id` from `reader`.
40    ///
41    /// NOTICE: `reader` has no chunk header (i.e., the identifier and data size of the chunk).
42    fn decode_data<R: Read>(id: &Id, reader: R) -> Result<Self>
43    where
44        Self: Sized;
45
46    /// Writes the chunk to `writer`.
47    fn encode<W: Write>(&self, mut writer: W) -> Result<()> {
48        let mut buf = Vec::new();
49        self.encode_data(&mut buf)?;
50        aux::Header::new(self.id(), buf.len() as u32).encode(&mut writer)?;
51        writer.write_all(&buf)?;
52        for _ in 0..aux::padding_size(buf.len() as u32) {
53            writer.write_u8(0)?;
54        }
55        Ok(())
56    }
57
58    /// Writes the data of the chunk to `writer`.
59    ///
60    /// NOTICE: The header (i.e., identifier and data size) of
61    /// the chunk must not write in the function.
62    fn encode_data<W: Write>(&self, writer: W) -> Result<()>;
63}
64
65/// A raw representation of a chunk.
66///
67/// This implementation does not interpret the data of a chunk
68/// at the time of reading it from a BEAM file.
69#[derive(Debug, PartialEq, Eq)]
70pub struct RawChunk {
71    /// The identifier of the chunk.
72    pub id: Id,
73
74    /// The bare data of the chunk.
75    pub data: Vec<u8>,
76}
77impl Chunk for RawChunk {
78    fn id(&self) -> &Id {
79        &self.id
80    }
81    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
82    where
83        Self: Sized,
84    {
85        let mut buf = Vec::new();
86        reader.read_to_end(&mut buf)?;
87        Ok(RawChunk { id: *id, data: buf })
88    }
89    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
90        writer.write_all(&self.data)?;
91        Ok(())
92    }
93}
94
95/// A representation of the `"Atom"` and `"AtU8"` chunks.
96#[derive(Debug, PartialEq, Eq)]
97pub struct AtomChunk {
98    // Whether or not this Atom chunk contains UTF-8 atoms
99    pub is_unicode: bool,
100    /// The list of atoms contained in a BEAM file.
101    pub atoms: Vec<parts::Atom>,
102}
103impl Chunk for AtomChunk {
104    fn id(&self) -> &Id {
105        if self.is_unicode {
106            b"AtU8"
107        } else {
108            b"Atom"
109        }
110    }
111    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
112    where
113        Self: Sized,
114    {
115        // This chunk can be either Atom or AtU8
116        let unicode = match aux::check_chunk_id(id, b"Atom") {
117            Err(_) => {
118                aux::check_chunk_id(id, b"AtU8")?;
119                true
120            }
121            Ok(_) => false,
122        };
123        let count = reader.read_u32::<BigEndian>()? as usize;
124        let mut atoms = Vec::with_capacity(count);
125        for _ in 0..count {
126            let len = reader.read_u8()? as usize;
127            let mut buf = vec![0; len];
128            reader.read_exact(&mut buf)?;
129
130            let name = str::from_utf8(&buf).map(|s| s.to_string())?;
131            atoms.push(parts::Atom {
132                name: name.to_string(),
133            });
134        }
135        Ok(AtomChunk {
136            is_unicode: unicode,
137            atoms,
138        })
139    }
140    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
141        writer.write_u32::<BigEndian>(self.atoms.len() as u32)?;
142        for atom in &self.atoms {
143            assert!(atom.name.len() < 0x100);
144            writer.write_u8(atom.name.len() as u8)?;
145            writer.write_all(atom.name.as_bytes())?;
146        }
147        Ok(())
148    }
149}
150
151/// A representation of the `"Code"` chunk.
152#[derive(Debug, PartialEq, Eq)]
153pub struct CodeChunk {
154    /// Length of the information fields before code.
155    pub info_size: u32,
156
157    /// Instruction set version.
158    pub version: u32,
159
160    /// The highest opcode used in the code section.
161    pub opcode_max: u32,
162
163    /// The number of labels.
164    pub label_count: u32,
165
166    /// The number of functions.
167    pub function_count: u32,
168
169    /// The byte code.
170    pub bytecode: Vec<u8>,
171}
172impl Chunk for CodeChunk {
173    fn id(&self) -> &Id {
174        b"Code"
175    }
176    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
177    where
178        Self: Sized,
179    {
180        aux::check_chunk_id(id, b"Code")?;
181        let mut code = CodeChunk {
182            info_size: reader.read_u32::<BigEndian>()?,
183            version: reader.read_u32::<BigEndian>()?,
184            opcode_max: reader.read_u32::<BigEndian>()?,
185            label_count: reader.read_u32::<BigEndian>()?,
186            function_count: reader.read_u32::<BigEndian>()?,
187            bytecode: Vec::new(),
188        };
189        reader.read_to_end(&mut code.bytecode)?;
190        Ok(code)
191    }
192    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
193        writer.write_u32::<BigEndian>(self.info_size)?;
194        writer.write_u32::<BigEndian>(self.version)?;
195        writer.write_u32::<BigEndian>(self.opcode_max)?;
196        writer.write_u32::<BigEndian>(self.label_count)?;
197        writer.write_u32::<BigEndian>(self.function_count)?;
198        writer.write_all(&self.bytecode)?;
199        Ok(())
200    }
201}
202
203/// A representation of the `"StrT"` chunk.
204#[derive(Debug, PartialEq, Eq)]
205pub struct StrTChunk {
206    /// Concatenated strings.
207    pub strings: Vec<u8>,
208}
209impl Chunk for StrTChunk {
210    fn id(&self) -> &Id {
211        b"StrT"
212    }
213    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
214    where
215        Self: Sized,
216    {
217        aux::check_chunk_id(id, b"StrT")?;
218        let mut buf = Vec::new();
219        reader.read_to_end(&mut buf)?;
220        Ok(StrTChunk { strings: buf })
221    }
222    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
223        writer.write_all(&self.strings)?;
224        Ok(())
225    }
226}
227
228/// A representation of the `"ImpT"` chunk.
229#[derive(Debug, PartialEq, Eq)]
230pub struct ImpTChunk {
231    /// The list of imported functions.
232    pub imports: Vec<parts::Import>,
233}
234impl Chunk for ImpTChunk {
235    fn id(&self) -> &Id {
236        b"ImpT"
237    }
238    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
239    where
240        Self: Sized,
241    {
242        aux::check_chunk_id(id, b"ImpT")?;
243        let count = reader.read_u32::<BigEndian>()? as usize;
244        let mut imports = Vec::with_capacity(count);
245        for _ in 0..count {
246            imports.push(parts::Import {
247                module: reader.read_u32::<BigEndian>()?,
248                function: reader.read_u32::<BigEndian>()?,
249                arity: reader.read_u32::<BigEndian>()?,
250            });
251        }
252        Ok(ImpTChunk { imports })
253    }
254    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
255        writer.write_u32::<BigEndian>(self.imports.len() as u32)?;
256        for import in &self.imports {
257            writer.write_u32::<BigEndian>(import.module)?;
258            writer.write_u32::<BigEndian>(import.function)?;
259            writer.write_u32::<BigEndian>(import.arity)?;
260        }
261        Ok(())
262    }
263}
264
265/// A representation of the `"ExpT"` chunk.
266#[derive(Debug, PartialEq, Eq)]
267pub struct ExpTChunk {
268    /// The list of exported functions.
269    pub exports: Vec<parts::Export>,
270}
271impl Chunk for ExpTChunk {
272    fn id(&self) -> &Id {
273        b"ExpT"
274    }
275    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
276    where
277        Self: Sized,
278    {
279        aux::check_chunk_id(id, b"ExpT")?;
280        let count = reader.read_u32::<BigEndian>()? as usize;
281        let mut exports = Vec::with_capacity(count);
282        for _ in 0..count {
283            exports.push(parts::Export {
284                function: reader.read_u32::<BigEndian>()?,
285                arity: reader.read_u32::<BigEndian>()?,
286                label: reader.read_u32::<BigEndian>()?,
287            });
288        }
289        Ok(ExpTChunk { exports })
290    }
291    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
292        writer.write_u32::<BigEndian>(self.exports.len() as u32)?;
293        for export in &self.exports {
294            writer.write_u32::<BigEndian>(export.function)?;
295            writer.write_u32::<BigEndian>(export.arity)?;
296            writer.write_u32::<BigEndian>(export.label)?;
297        }
298        Ok(())
299    }
300}
301
302/// A representation of the `"LitT"` chunk.
303#[derive(Debug, PartialEq, Eq)]
304pub struct LitTChunk {
305    /// The list of literal terms.
306    ///
307    /// Each term is encoded in the [External Term Format]
308    /// (http://erlang.org/doc/apps/erts/erl_ext_dist.html).
309    pub literals: Vec<parts::ExternalTermFormatBinary>,
310}
311impl Chunk for LitTChunk {
312    fn id(&self) -> &Id {
313        b"LitT"
314    }
315    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
316    where
317        Self: Sized,
318    {
319        aux::check_chunk_id(id, b"LitT")?;
320        let _uncompressed_size = reader.read_u32::<BigEndian>()?;
321        let mut decoder = zlib::Decoder::new(reader)?;
322
323        let count = decoder.read_u32::<BigEndian>()? as usize;
324        let mut literals = Vec::with_capacity(count);
325        for _ in 0..count {
326            let literal_size = decoder.read_u32::<BigEndian>()? as usize;
327            let mut buf = vec![0; literal_size];
328            decoder.read_exact(&mut buf)?;
329            literals.push(buf);
330        }
331        Ok(LitTChunk { literals })
332    }
333    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
334        let uncompressed_size = self
335            .literals
336            .iter()
337            .fold(4, |acc, l| acc + 4 + l.len() as u32);
338        writer.write_u32::<BigEndian>(uncompressed_size)?;
339
340        let mut encoder = zlib::Encoder::new(writer)?;
341        encoder.write_u32::<BigEndian>(self.literals.len() as u32)?;
342        for literal in &self.literals {
343            encoder.write_u32::<BigEndian>(literal.len() as u32)?;
344            encoder.write_all(literal)?;
345        }
346        encoder.finish().into_result()?;
347        Ok(())
348    }
349}
350
351/// A representation of the `"LocT"` chunk.
352#[derive(Debug, PartialEq, Eq)]
353pub struct LocTChunk {
354    /// The list of local functions.
355    pub locals: Vec<parts::Local>,
356}
357impl Chunk for LocTChunk {
358    fn id(&self) -> &Id {
359        b"LocT"
360    }
361    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
362    where
363        Self: Sized,
364    {
365        aux::check_chunk_id(id, b"LocT")?;
366        let count = reader.read_u32::<BigEndian>()? as usize;
367        let mut locals = Vec::with_capacity(count);
368        for _ in 0..count {
369            locals.push(parts::Local {
370                function: reader.read_u32::<BigEndian>()?,
371                arity: reader.read_u32::<BigEndian>()?,
372                label: reader.read_u32::<BigEndian>()?,
373            });
374        }
375        Ok(LocTChunk { locals })
376    }
377    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
378        writer.write_u32::<BigEndian>(self.locals.len() as u32)?;
379        for local in &self.locals {
380            writer.write_u32::<BigEndian>(local.function)?;
381            writer.write_u32::<BigEndian>(local.arity)?;
382            writer.write_u32::<BigEndian>(local.label)?;
383        }
384        Ok(())
385    }
386}
387
388/// A representation of the `"FunT"` chunk.
389#[derive(Debug, PartialEq, Eq)]
390pub struct FunTChunk {
391    /// The list of anonymous functions.
392    pub functions: Vec<parts::Function>,
393}
394impl Chunk for FunTChunk {
395    fn id(&self) -> &Id {
396        b"FunT"
397    }
398    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
399    where
400        Self: Sized,
401    {
402        aux::check_chunk_id(id, b"FunT")?;
403        let count = reader.read_u32::<BigEndian>()? as usize;
404        let mut functions = Vec::with_capacity(count);
405        for _ in 0..count {
406            functions.push(parts::Function {
407                function: reader.read_u32::<BigEndian>()?,
408                arity: reader.read_u32::<BigEndian>()?,
409                label: reader.read_u32::<BigEndian>()?,
410                index: reader.read_u32::<BigEndian>()?,
411                num_free: reader.read_u32::<BigEndian>()?,
412                old_uniq: reader.read_u32::<BigEndian>()?,
413            });
414        }
415        Ok(FunTChunk { functions })
416    }
417    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
418        writer.write_u32::<BigEndian>(self.functions.len() as u32)?;
419        for f in &self.functions {
420            writer.write_u32::<BigEndian>(f.function)?;
421            writer.write_u32::<BigEndian>(f.arity)?;
422            writer.write_u32::<BigEndian>(f.label)?;
423            writer.write_u32::<BigEndian>(f.index)?;
424            writer.write_u32::<BigEndian>(f.num_free)?;
425            writer.write_u32::<BigEndian>(f.old_uniq)?;
426        }
427        Ok(())
428    }
429}
430
431/// A representation of the `"Attr"` chunk.
432#[derive(Debug, PartialEq, Eq)]
433pub struct AttrChunk {
434    /// The attributes of a module (i.e., BEAM file).
435    ///
436    /// The value is equivalent to the result of the following erlang code.
437    /// ```erlang
438    /// term_to_binary(Module:module_info(attributes)).
439    /// ```
440    pub term: parts::ExternalTermFormatBinary,
441}
442impl Chunk for AttrChunk {
443    fn id(&self) -> &Id {
444        b"Attr"
445    }
446    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
447    where
448        Self: Sized,
449    {
450        aux::check_chunk_id(id, b"Attr")?;
451        let mut buf = Vec::new();
452        reader.read_to_end(&mut buf)?;
453        Ok(AttrChunk { term: buf })
454    }
455    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
456        writer.write_all(&self.term)?;
457        Ok(())
458    }
459}
460
461/// A representation of the `"CInf"` chunk.
462#[derive(Debug, PartialEq, Eq)]
463pub struct CInfChunk {
464    /// The compile information of a module (i.e., BEAM file).
465    ///
466    /// The value is equivalent to the result of the following erlang code.
467    /// ```erlang
468    /// term_to_binary(Module:module_info(compile)).
469    /// ```
470    pub term: parts::ExternalTermFormatBinary,
471}
472impl Chunk for CInfChunk {
473    fn id(&self) -> &Id {
474        b"CInf"
475    }
476    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
477    where
478        Self: Sized,
479    {
480        aux::check_chunk_id(id, b"CInf")?;
481        let mut buf = Vec::new();
482        reader.read_to_end(&mut buf)?;
483        Ok(CInfChunk { term: buf })
484    }
485    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
486        writer.write_all(&self.term)?;
487        Ok(())
488    }
489}
490
491/// A representation of the `"Abst"` chunk.
492#[derive(Debug, PartialEq, Eq)]
493pub struct AbstChunk {
494    /// The abstract code of a module (i.e., BEAM file).
495    ///
496    /// The value is encoded in the [External Term Format]
497    /// (http://erlang.org/doc/apps/erts/erl_ext_dist.html) and
498    /// represents [The Abstract Format](http://erlang.org/doc/apps/erts/absform.html).
499    pub term: parts::ExternalTermFormatBinary,
500}
501impl Chunk for AbstChunk {
502    fn id(&self) -> &Id {
503        b"Abst"
504    }
505    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
506    where
507        Self: Sized,
508    {
509        aux::check_chunk_id(id, b"Abst")?;
510        let mut buf = Vec::new();
511        reader.read_to_end(&mut buf)?;
512        Ok(AbstChunk { term: buf })
513    }
514    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
515        writer.write_all(&self.term)?;
516        Ok(())
517    }
518}
519
520/// A representation of the `"Dbgi"` chunk.
521#[derive(Debug, PartialEq, Eq)]
522pub struct DbgiChunk {
523    /// The debug information for a module (i.e., BEAM file).
524    ///
525    /// Supercedes 'Abst' in recent versions of OTP by supporting arbitrary abstract formats.
526    ///
527    /// The value is encoded in the [External Term Format]
528    /// (http://erlang.org/doc/apps/erts/erl_ext_dist.html) and
529    /// represents custom debug information in the following term format:
530    ///
531    /// ```erlang
532    /// {debug_info, {Backend, Data}}
533    /// ```
534    ///
535    /// Where `Backend` is a module which implements `debug_info/4`, and is responsible for
536    /// converting `Data` to different representations as described [here](http://erlang.org/doc/man/beam_lib.html#type-debug_info).
537    /// Debug information can be used to reconstruct original source code.
538    pub term: parts::ExternalTermFormatBinary,
539}
540impl Chunk for DbgiChunk {
541    fn id(&self) -> &Id {
542        b"Dbgi"
543    }
544    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
545    where
546        Self: Sized,
547    {
548        aux::check_chunk_id(id, b"Dbgi")?;
549        let mut buf = Vec::new();
550        reader.read_to_end(&mut buf)?;
551        Ok(DbgiChunk { term: buf })
552    }
553    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
554        writer.write_all(&self.term)?;
555        Ok(())
556    }
557}
558
559/// A representation of the `"Docs"` chunk.
560#[derive(Debug, PartialEq, Eq)]
561pub struct DocsChunk {
562    /// The 'Docs' chunk contains embedded module documentation, such as moduledoc/doc in Elixir
563    ///
564    /// The value is encoded in the [External Term Format]
565    /// (http://erlang.org/doc/apps/erts/erl_ext_dist.html) and
566    /// represents a term in the following format:
567    ///
568    /// ```erlang
569    /// {Module, [{"Docs", DocsBin}]}
570    /// ```
571    ///
572    /// Where `Module` is the documented module, and `DocsBin` is a binary in External Term Format
573    /// containing the documentation. Currently, that decodes to:
574    ///
575    /// ```erlang
576    /// {docs_v1, Anno, BeamLang, Format, ModuleDoc, Metadata, Docs}
577    ///   where Anno :: erl_anno:anno(),
578    ///         BeamLang :: erlang | elixir | lfe | alpaca | atom(),
579    ///         Format :: binary(),
580    ///         ModuleDoc :: doc_content(),
581    ///         Metadata :: map(),
582    ///         Docs :: [doc_element()],
583    ///         signature :: [binary],
584    ///         doc_content :: map(binary(), binary()) | none | hidden,
585    ///         doc_element :: {{kind :: atom(), function :: atom(), arity}, Anno, signature, doc_content(), Metadata}
586    /// ```
587    ///
588    pub term: parts::ExternalTermFormatBinary,
589}
590impl Chunk for DocsChunk {
591    fn id(&self) -> &Id {
592        b"Docs"
593    }
594    fn decode_data<R: Read>(id: &Id, mut reader: R) -> Result<Self>
595    where
596        Self: Sized,
597    {
598        aux::check_chunk_id(id, b"Docs")?;
599        let mut buf = Vec::new();
600        reader.read_to_end(&mut buf)?;
601        Ok(DocsChunk { term: buf })
602    }
603    fn encode_data<W: Write>(&self, mut writer: W) -> Result<()> {
604        writer.write_all(&self.term)?;
605        Ok(())
606    }
607}
608
609/// A representation of commonly used chunk.
610///
611/// ```
612/// use beam_file::BeamFile;
613/// use beam_file::chunk::{Chunk, StandardChunk};
614///
615/// let beam = BeamFile::<StandardChunk>::from_file("tests/testdata/test.beam").unwrap();
616/// assert_eq!(b"Atom", beam.chunks.iter().nth(0).map(|c| c.id()).unwrap());
617/// ```
618#[derive(Debug, PartialEq, Eq)]
619pub enum StandardChunk {
620    Atom(AtomChunk),
621    Code(CodeChunk),
622    StrT(StrTChunk),
623    ImpT(ImpTChunk),
624    ExpT(ExpTChunk),
625    LitT(LitTChunk),
626    LocT(LocTChunk),
627    FunT(FunTChunk),
628    Attr(AttrChunk),
629    CInf(CInfChunk),
630    Abst(AbstChunk),
631    Dbgi(DbgiChunk),
632    Docs(DocsChunk),
633    Unknown(RawChunk),
634}
635impl Chunk for StandardChunk {
636    fn id(&self) -> &Id {
637        use self::StandardChunk::*;
638        match *self {
639            Atom(ref c) => c.id(),
640            Code(ref c) => c.id(),
641            StrT(ref c) => c.id(),
642            ImpT(ref c) => c.id(),
643            ExpT(ref c) => c.id(),
644            LitT(ref c) => c.id(),
645            LocT(ref c) => c.id(),
646            FunT(ref c) => c.id(),
647            Attr(ref c) => c.id(),
648            CInf(ref c) => c.id(),
649            Abst(ref c) => c.id(),
650            Dbgi(ref c) => c.id(),
651            Docs(ref c) => c.id(),
652            Unknown(ref c) => c.id(),
653        }
654    }
655    fn decode_data<R: Read>(id: &Id, reader: R) -> Result<Self>
656    where
657        Self: Sized,
658    {
659        use self::StandardChunk::*;
660        match id {
661            b"Atom" => Ok(Atom(AtomChunk::decode_data(id, reader)?)),
662            b"AtU8" => Ok(Atom(AtomChunk::decode_data(id, reader)?)),
663            b"Code" => Ok(Code(CodeChunk::decode_data(id, reader)?)),
664            b"StrT" => Ok(StrT(StrTChunk::decode_data(id, reader)?)),
665            b"ImpT" => Ok(ImpT(ImpTChunk::decode_data(id, reader)?)),
666            b"ExpT" => Ok(ExpT(ExpTChunk::decode_data(id, reader)?)),
667            b"LitT" => Ok(LitT(LitTChunk::decode_data(id, reader)?)),
668            b"LocT" => Ok(LocT(LocTChunk::decode_data(id, reader)?)),
669            b"FunT" => Ok(FunT(FunTChunk::decode_data(id, reader)?)),
670            b"Attr" => Ok(Attr(AttrChunk::decode_data(id, reader)?)),
671            b"CInf" => Ok(CInf(CInfChunk::decode_data(id, reader)?)),
672            b"Abst" => Ok(Abst(AbstChunk::decode_data(id, reader)?)),
673            b"Dbgi" => Ok(Dbgi(DbgiChunk::decode_data(id, reader)?)),
674            b"Docs" => Ok(Docs(DocsChunk::decode_data(id, reader)?)),
675            _ => Ok(Unknown(RawChunk::decode_data(id, reader)?)),
676        }
677    }
678    fn encode_data<W: Write>(&self, writer: W) -> Result<()> {
679        use self::StandardChunk::*;
680        match *self {
681            Atom(ref c) => c.encode_data(writer),
682            Code(ref c) => c.encode_data(writer),
683            StrT(ref c) => c.encode_data(writer),
684            ImpT(ref c) => c.encode_data(writer),
685            ExpT(ref c) => c.encode_data(writer),
686            LitT(ref c) => c.encode_data(writer),
687            LocT(ref c) => c.encode_data(writer),
688            FunT(ref c) => c.encode_data(writer),
689            Attr(ref c) => c.encode_data(writer),
690            CInf(ref c) => c.encode_data(writer),
691            Abst(ref c) => c.encode_data(writer),
692            Dbgi(ref c) => c.encode_data(writer),
693            Docs(ref c) => c.encode_data(writer),
694            Unknown(ref c) => c.encode_data(writer),
695        }
696    }
697}
698
699mod aux {
700    use super::*;
701    use byteorder::BigEndian;
702    use byteorder::ReadBytesExt;
703    use byteorder::WriteBytesExt;
704    use std::io;
705
706    pub struct Header {
707        pub chunk_id: Id,
708        pub data_size: u32,
709    }
710    impl Header {
711        pub fn new(chunk_id: &Id, data_size: u32) -> Self {
712            Header {
713                chunk_id: *chunk_id,
714                data_size,
715            }
716        }
717        pub fn decode<R: io::Read>(mut reader: R) -> io::Result<Self> {
718            let mut id = [0; 4];
719            reader.read_exact(&mut id)?;
720            let size = reader.read_u32::<BigEndian>()?;
721            Ok(Header::new(&id, size))
722        }
723        pub fn encode<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
724            writer.write_all(&self.chunk_id)?;
725            writer.write_u32::<BigEndian>(self.data_size)?;
726            Ok(())
727        }
728    }
729
730    pub fn padding_size(data_size: u32) -> u32 {
731        (4 - data_size % 4) % 4
732    }
733
734    pub fn check_chunk_id(passed: &Id, expected: &Id) -> crate::Result<()> {
735        if passed != expected {
736            Err(crate::Error::UnexpectedChunk {
737                id: *passed,
738                expected: *expected,
739            })
740        } else {
741            Ok(())
742        }
743    }
744}