1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
9use libflate::zlib;
10use std::io::{Cursor, Read, Write};
11use std::str;
12
13use crate::parts;
14use crate::Result;
15
16pub type Id = [u8; 4];
18
19pub trait Chunk {
21 fn id(&self) -> &Id;
23
24 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 fn decode_data<R: Read>(id: &Id, reader: R) -> Result<Self>
43 where
44 Self: Sized;
45
46 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 fn encode_data<W: Write>(&self, writer: W) -> Result<()>;
63}
64
65#[derive(Debug, PartialEq, Eq)]
70pub struct RawChunk {
71 pub id: Id,
73
74 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#[derive(Debug, PartialEq, Eq)]
97pub struct AtomChunk {
98 pub is_unicode: bool,
100 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 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#[derive(Debug, PartialEq, Eq)]
153pub struct CodeChunk {
154 pub info_size: u32,
156
157 pub version: u32,
159
160 pub opcode_max: u32,
162
163 pub label_count: u32,
165
166 pub function_count: u32,
168
169 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#[derive(Debug, PartialEq, Eq)]
205pub struct StrTChunk {
206 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#[derive(Debug, PartialEq, Eq)]
230pub struct ImpTChunk {
231 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#[derive(Debug, PartialEq, Eq)]
267pub struct ExpTChunk {
268 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#[derive(Debug, PartialEq, Eq)]
304pub struct LitTChunk {
305 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#[derive(Debug, PartialEq, Eq)]
353pub struct LocTChunk {
354 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#[derive(Debug, PartialEq, Eq)]
390pub struct FunTChunk {
391 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#[derive(Debug, PartialEq, Eq)]
433pub struct AttrChunk {
434 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#[derive(Debug, PartialEq, Eq)]
463pub struct CInfChunk {
464 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#[derive(Debug, PartialEq, Eq)]
493pub struct AbstChunk {
494 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#[derive(Debug, PartialEq, Eq)]
522pub struct DbgiChunk {
523 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#[derive(Debug, PartialEq, Eq)]
561pub struct DocsChunk {
562 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#[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}