spectrusty_formats/tap/
write.rs1use core::mem;
9use core::slice;
10use core::convert::{TryFrom, TryInto};
11use core::num::NonZeroU32;
12use std::io::{ErrorKind, Error, Write, Seek, SeekFrom, Result};
13use super::pulse::PulseDecodeWriter;
14use super::{Header, checksum};
15
16const LEN_PREFIX_SIZE: u64 = mem::size_of::<u16>() as u64;
17
18pub struct TapChunkWriter<W> {
26 chunk_head: u64,
27 mpwr: PulseDecodeWriter<W>
28}
29
30pub struct TapChunkWriteTran<'a, W: Write + Seek> {
35 pub checksum: u8,
37 nchunks: usize,
38 uncommitted: u16,
39 writer: &'a mut TapChunkWriter<W>
40}
41
42impl<W> TapChunkWriter<W> {
43 pub fn into_inner(self) -> PulseDecodeWriter<W> {
45 self.mpwr
46 }
47 pub fn get_mut(&mut self) -> &mut PulseDecodeWriter<W> {
49 &mut self.mpwr
50 }
51 pub fn get_ref(&self) -> &PulseDecodeWriter<W> {
53 &self.mpwr
54 }
55}
56
57impl<'a, W: Write + Seek> Drop for TapChunkWriteTran<'a, W> {
58 fn drop(&mut self) {
61 if self.uncommitted != 0 {
62 let chunk_head = self.writer.chunk_head.checked_add(LEN_PREFIX_SIZE).unwrap();
63 let _ = self.writer.mpwr.get_mut().seek(SeekFrom::Start(chunk_head));
64 }
65 }
66}
67
68impl<'a, W: Write + Seek> Write for TapChunkWriteTran<'a, W> {
69 fn write(&mut self, buf: &[u8]) -> Result<usize> {
73 let _: u16 = (self.uncommitted as usize).checked_add(buf.len()).unwrap()
74 .try_into().map_err(|e| Error::new(ErrorKind::WriteZero, e))?;
75 let written = self.writer.mpwr.get_mut().write(buf)?;
76 self.checksum ^= checksum(&buf[..written]);
77 self.uncommitted += written as u16;
78 Ok(written)
79 }
80
81 fn flush(&mut self) -> Result<()> {
82 self.writer.flush()
83 }
84}
85
86impl<'a, W> TapChunkWriteTran<'a, W>
87 where W: Write + Seek
88{
89 pub fn commit(mut self, with_checksum: bool) -> Result<usize> {
95 let mut nchunks = self.nchunks;
96 let mut uncommitted = self.uncommitted;
97 if with_checksum {
98 if let Some(size) = uncommitted.checked_add(1) {
99 uncommitted = size;
100 let checksum = self.checksum;
101 self.write_all(slice::from_ref(&checksum))?;
102 }
103 else {
104 return Err(Error::new(ErrorKind::WriteZero, "chunk is larger than the maximum allowed size"))
105 }
106 }
107 if let Some(size) = NonZeroU32::new(uncommitted.into()) {
108 self.writer.commit_chunk(size)?;
109 nchunks += 1;
110 }
111 Ok(nchunks)
112 }
113}
114
115impl<W> TapChunkWriter<W>
116 where W: Write + Seek
117{
118 pub fn try_new(wr: W) -> Result<Self> {
125 let mut mpwr = PulseDecodeWriter::new(wr);
126 let chunk_start = mpwr.get_mut().seek(SeekFrom::Current(LEN_PREFIX_SIZE as i64))?;
127 let chunk_head = chunk_start.checked_sub(LEN_PREFIX_SIZE).unwrap();
128 Ok(TapChunkWriter { chunk_head, mpwr })
129 }
130 pub fn flush(&mut self) -> Result<()> {
133 self.mpwr.get_mut().flush()
134 }
135 pub fn end_pulse_chunk(&mut self) -> Result<usize> {
139 if let Some(size) = self.mpwr.end()? {
140 self.commit_chunk(size)?;
141 Ok(1)
142 }
143 else {
144 Ok(0)
145 }
146 }
147 pub fn write_header(&mut self, header: &Header) -> Result<usize> {
153 self.write_chunk(header.to_tap_chunk())
154 }
155 pub fn write_chunk<D: AsRef<[u8]>>(&mut self, chunk: D) -> Result<usize> {
161 let data = chunk.as_ref();
162 let size = u16::try_from(data.len()).map_err(|_|
163 Error::new(ErrorKind::InvalidData, "TAP chunk too large."))?;
164 let nchunks = self.end_pulse_chunk()?;
165 let wr = self.mpwr.get_mut();
166 let chunk_head = wr.seek(SeekFrom::Start(self.chunk_head))?;
167 debug_assert_eq!(chunk_head, self.chunk_head);
168 wr.write_all(&size.to_le_bytes())?;
169 wr.write_all(data)?;
170 let chunk_start = wr.seek(SeekFrom::Current(LEN_PREFIX_SIZE as i64))?;
171 self.chunk_head = chunk_start.checked_sub(LEN_PREFIX_SIZE).unwrap();
172 Ok(nchunks + 1)
173 }
174 pub fn begin(&mut self) -> Result<TapChunkWriteTran<'_, W>> {
180 let nchunks = self.end_pulse_chunk()?;
181 Ok(TapChunkWriteTran { checksum: 0, nchunks, uncommitted: 0, writer: self })
182 }
183 pub fn write_pulses_as_tap_chunks<I>(&mut self, mut iter: I) -> Result<usize>
190 where I: Iterator<Item=NonZeroU32>
191 {
192 let mut chunks = 0;
193 loop {
194 match self.mpwr.write_decoded_pulses(iter.by_ref())? {
195 None => return Ok(chunks),
196 Some(size) => {
197 chunks += 1;
198 self.commit_chunk(size)?;
199 }
200 }
201 }
202 }
203
204 fn commit_chunk(&mut self, size: NonZeroU32) -> Result<()> {
205 let size = u16::try_from(size.get()).map_err(|_|
206 Error::new(ErrorKind::InvalidData, "TAP chunk too large."))?;
207 let wr = self.mpwr.get_mut();
209 let chunk_head = wr.seek(SeekFrom::Start(self.chunk_head))?;
210 debug_assert_eq!(chunk_head, self.chunk_head);
211 wr.write_all(&size.to_le_bytes())?;
212 self.chunk_head = chunk_head.checked_add(LEN_PREFIX_SIZE + size as u64).unwrap();
213 let pos_cur = self.chunk_head.checked_add(LEN_PREFIX_SIZE).unwrap();
214 let pos_next = wr.seek(SeekFrom::Start(pos_cur))?;
215 debug_assert_eq!(pos_next, pos_cur);
216 Ok(())
217 }
218}