1use crate::{
4 block::Block,
5 ext::{Tell, WriteBlock},
6 headers::{ChunkHeader, ChunkType, FileHeader},
7 result::Result,
8};
9use byteorder::{LittleEndian, WriteBytesExt};
10use crc::crc32::{self, Hasher32};
11use std::io::{prelude::*, BufWriter, SeekFrom};
12
13pub struct Writer<W: Write + Seek> {
15 dst: BufWriter<W>,
16 current_chunk: Option<ChunkHeader>,
17 current_fill: Option<[u8; 4]>,
18 num_blocks: u32,
19 num_chunks: u32,
20 crc: Option<crc32::Digest>,
21 finished: bool,
22}
23
24impl<W: Write + Seek> Writer<W> {
25 pub fn new(w: W) -> Result<Self> {
31 let mut dst = BufWriter::new(w);
32 dst.seek(SeekFrom::Current(i64::from(FileHeader::SIZE)))?;
36
37 Ok(Self {
38 dst,
39 current_chunk: None,
40 current_fill: None,
41 num_blocks: 0,
42 num_chunks: 0,
43 crc: None,
44 finished: false,
45 })
46 }
47
48 pub fn with_crc(w: W) -> Result<Self> {
51 let mut writer = Self::new(w)?;
52 writer.crc = Some(crc32::Digest::new(crc32::IEEE));
53 Ok(writer)
54 }
55
56 pub fn write_block(&mut self, block: &Block) -> Result<()> {
61 if !self.can_merge(block) {
62 self.finish_chunk()?;
63 self.start_chunk(block)?;
64 }
65
66 let chunk = self.current_chunk.as_mut().unwrap();
67
68 match block {
69 Block::Raw(buf) => {
70 self.dst.write_all(&**buf)?;
71 chunk.total_size += Block::SIZE;
72 }
73 Block::Fill(value) => {
74 if self.current_fill.is_none() {
75 self.dst.write_all(value)?;
76 self.current_fill = Some(*value);
77 }
78 }
79 Block::Skip => (),
80 Block::Crc32(checksum) => {
81 self.dst.write_u32::<LittleEndian>(*checksum)?;
82 return Ok(());
84 }
85 }
86
87 if let Some(digest) = self.crc.as_mut() {
88 digest.write_block(block);
89 }
90
91 chunk.chunk_size += 1;
92 Ok(())
93 }
94
95 pub fn close(mut self) -> Result<()> {
99 self.finish()
100 }
101
102 fn finish(&mut self) -> Result<()> {
103 assert!(!self.finished);
104 self.finished = true;
105
106 self.write_checksum()?;
107 self.finish_chunk()?;
108
109 let image_checksum = 0;
113 let header = FileHeader {
114 total_blocks: self.num_blocks,
115 total_chunks: self.num_chunks,
116 image_checksum,
117 };
118
119 self.dst.seek(SeekFrom::Start(0))?;
120 header.write_to(&mut self.dst)?;
121
122 self.dst.flush()?;
123 Ok(())
124 }
125
126 fn can_merge(&self, block: &Block) -> bool {
127 let chunk = match self.current_chunk.as_ref() {
128 Some(c) => c,
129 None => return false,
130 };
131
132 match (chunk.chunk_type, block) {
133 (ChunkType::Raw, Block::Raw(_)) | (ChunkType::DontCare, Block::Skip) => true,
134 (ChunkType::Fill, Block::Fill(value)) => self.current_fill.unwrap() == *value,
135 _ => false,
136 }
137 }
138
139 fn start_chunk(&mut self, block: &Block) -> Result<()> {
140 assert!(self.current_chunk.is_none());
141
142 let (chunk_type, init_size) = match block {
143 Block::Raw(_) => (ChunkType::Raw, 0),
144 Block::Fill(_) => (ChunkType::Fill, 4),
145 Block::Skip => (ChunkType::DontCare, 0),
146 Block::Crc32(_) => (ChunkType::Crc32, 4),
147 };
148
149 let chunk = ChunkHeader {
150 chunk_type,
151 chunk_size: 0,
152 total_size: init_size + u32::from(ChunkHeader::SIZE),
153 };
154
155 let header_size = i64::from(ChunkHeader::SIZE);
159 self.dst.seek(SeekFrom::Current(header_size))?;
160
161 self.current_chunk = Some(chunk);
162
163 Ok(())
164 }
165
166 fn finish_chunk(&mut self) -> Result<()> {
167 let chunk = match self.current_chunk.take() {
168 Some(c) => c,
169 None => return Ok(()),
170 };
171
172 let pos = self.dst.tell()?;
173 let header_off = i64::from(chunk.total_size);
174 self.dst.seek(SeekFrom::Current(-header_off))?;
175 chunk.write_to(&mut self.dst)?;
176 self.dst.seek(SeekFrom::Start(pos))?;
177
178 self.current_fill = None;
179 self.num_chunks += 1;
180 self.num_blocks += chunk.chunk_size;
181
182 Ok(())
183 }
184
185 fn write_checksum(&mut self) -> Result<()> {
186 let checksum = match self.crc.as_ref() {
187 Some(digest) => digest.sum32(),
188 None => return Ok(()),
189 };
190
191 let block = Block::Crc32(checksum);
192 self.write_block(&block)
193 }
194}
195
196impl<W: Write + Seek> Drop for Writer<W> {
197 fn drop(&mut self) {
198 if !self.finished {
199 self.finish().ok();
200 }
201 }
202}
203
204pub struct Decoder<W: Write + Seek> {
206 dst: BufWriter<W>,
207 finished: bool,
208}
209
210impl<W: Write + Seek> Decoder<W> {
211 pub fn new(w: W) -> Result<Self> {
213 let dst = BufWriter::new(w);
214 Ok(Self {
215 dst,
216 finished: false,
217 })
218 }
219
220 pub fn write_block(&mut self, block: &Block) -> Result<()> {
225 match block {
226 Block::Raw(buf) => self.dst.write_all(&**buf)?,
227 Block::Fill(value) => {
228 let count = Block::SIZE as usize / 4;
229 for _ in 0..count {
230 self.dst.write_all(value)?;
231 }
232 }
233 Block::Skip => {
234 let offset = i64::from(Block::SIZE) - 1;
235 self.dst.seek(SeekFrom::Current(offset))?;
236 self.dst.write_all(&[0])?;
237 }
238 Block::Crc32(_) => (),
239 }
240 Ok(())
241 }
242
243 pub fn close(mut self) -> Result<()> {
247 self.finish()
248 }
249
250 fn finish(&mut self) -> Result<()> {
251 assert!(!self.finished);
252 self.finished = true;
253
254 let file = self.dst.get_mut();
257
258 file.flush()?;
259 Ok(())
260 }
261}
262
263impl<W: Write + Seek> Drop for Decoder<W> {
264 fn drop(&mut self) {
265 if !self.finished {
266 self.finish().ok();
267 }
268 }
269}