1use base64_ng::{Alphabet, Engine};
2use core::{
3 marker::PhantomData,
4 pin::Pin,
5 task::{Context, Poll, ready},
6};
7use tokio::io::{self, AsyncWrite};
8
9use crate::{decode_io_error, queue::OutputQueue, wipe_bytes};
10
11const DECODE_OUTPUT_CAP: usize = 1024;
12
13pub struct DecoderWriter<W, A, const PAD: bool>
28where
29 A: Alphabet,
30{
31 inner: Option<W>,
32 engine: Engine<A, PAD>,
33 pending: [u8; 4],
34 pending_len: usize,
35 output: OutputQueue<DECODE_OUTPUT_CAP>,
36 terminal_padding: bool,
37 finalized: bool,
38 failed: bool,
39 _alphabet: PhantomData<A>,
40}
41
42impl<W, A, const PAD: bool> DecoderWriter<W, A, PAD>
43where
44 A: Alphabet,
45{
46 #[must_use]
48 pub fn new(inner: W, engine: Engine<A, PAD>) -> Self {
49 Self {
50 inner: Some(inner),
51 engine,
52 pending: [0; 4],
53 pending_len: 0,
54 output: OutputQueue::new(),
55 terminal_padding: false,
56 finalized: false,
57 failed: false,
58 _alphabet: PhantomData,
59 }
60 }
61
62 #[must_use]
64 pub fn get_ref(&self) -> &W {
65 self.inner_ref()
66 }
67
68 pub fn get_mut(&mut self) -> &mut W {
70 self.inner_mut()
71 }
72
73 #[must_use]
79 pub fn into_inner(mut self) -> W {
80 self.take_inner()
81 }
82
83 #[must_use]
85 pub const fn is_failed(&self) -> bool {
86 self.failed
87 }
88
89 #[must_use]
91 pub const fn is_finalized(&self) -> bool {
92 self.finalized
93 }
94
95 #[must_use]
97 pub const fn has_terminal_padding(&self) -> bool {
98 self.terminal_padding
99 }
100
101 #[must_use]
104 pub const fn pending_len(&self) -> usize {
105 self.pending_len
106 }
107
108 #[must_use]
110 pub const fn buffered_output_len(&self) -> usize {
111 self.output.len()
112 }
113
114 fn clear_pending(&mut self) {
115 wipe_bytes(&mut self.pending);
116 self.pending_len = 0;
117 }
118
119 fn clear_output(&mut self) {
120 self.output.clear_all();
121 }
122
123 fn inner_ref(&self) -> &W {
124 match &self.inner {
125 Some(inner) => inner,
126 None => unreachable!("tokio decoder writer inner writer was already taken"),
127 }
128 }
129
130 fn inner_mut(&mut self) -> &mut W {
131 match &mut self.inner {
132 Some(inner) => inner,
133 None => unreachable!("tokio decoder writer inner writer was already taken"),
134 }
135 }
136
137 fn take_inner(&mut self) -> W {
138 match self.inner.take() {
139 Some(inner) => inner,
140 None => unreachable!("tokio decoder writer inner writer was already taken"),
141 }
142 }
143
144 fn queue_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
145 let written = match self.engine.decode_slice(input, decoded) {
146 Ok(written) => written,
147 Err(error) => {
148 wipe_bytes(decoded);
149 self.failed = true;
150 return Err(decode_io_error(error));
151 }
152 };
153
154 let result = self.output.push_slice(&decoded[..written]);
155 wipe_bytes(decoded);
156 if result.is_err() {
157 self.failed = true;
158 }
159 result?;
160 Ok(written)
161 }
162
163 fn queue_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
164 let mut decoded = [0u8; 3];
165 let result = self.queue_decoded_temp(&input, &mut decoded);
166 wipe_bytes(&mut input);
167 let written = result?;
168 if written < 3 {
169 self.terminal_padding = true;
170 }
171 Ok(())
172 }
173
174 fn queue_pending_final(&mut self) -> io::Result<()> {
175 if self.pending_len == 0 {
176 return Ok(());
177 }
178
179 if PAD || self.pending_len == 1 {
180 self.failed = true;
181 self.clear_pending();
182 return Err(io::Error::new(
183 io::ErrorKind::InvalidData,
184 "base64-ng-tokio decoder writer received incomplete final quantum",
185 ));
186 }
187
188 let mut pending = [0u8; 4];
189 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
190 let pending_len = self.pending_len;
191 let mut decoded = [0u8; 3];
192 let result = self.queue_decoded_temp(&pending[..pending_len], &mut decoded);
193 wipe_bytes(&mut pending);
194 result?;
195 self.clear_pending();
196 Ok(())
197 }
198
199 fn trailing_input_error(&mut self) -> io::Error {
200 self.failed = true;
201 io::Error::new(
202 io::ErrorKind::InvalidData,
203 "base64-ng-tokio decoder writer received trailing input after padding",
204 )
205 }
206
207 fn process_input(&mut self, input: &[u8]) -> io::Result<usize> {
208 if input.is_empty() {
209 return Ok(0);
210 }
211 if self.terminal_padding {
212 return Err(self.trailing_input_error());
213 }
214
215 let mut consumed = 0;
216 if self.pending_len > 0 {
217 let needed = 4 - self.pending_len;
218 if input.len() < needed {
219 self.pending[self.pending_len..self.pending_len + input.len()]
220 .copy_from_slice(input);
221 self.pending_len += input.len();
222 return Ok(input.len());
223 }
224
225 let mut quad = [0u8; 4];
226 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
227 quad[self.pending_len..].copy_from_slice(&input[..needed]);
228 let result = self.queue_full_quad(quad);
229 wipe_bytes(&mut quad);
230 if let Err(error) = result {
231 self.clear_pending();
232 return Err(error);
233 }
234 self.clear_pending();
235 consumed += needed;
236
237 if self.terminal_padding {
238 return Ok(consumed);
239 }
240 }
241
242 while input.len() - consumed >= 4 {
243 if self.output.available_capacity() < 3 {
244 return Ok(consumed);
245 }
246
247 let mut quad = [
248 input[consumed],
249 input[consumed + 1],
250 input[consumed + 2],
251 input[consumed + 3],
252 ];
253 let mut decoded = [0u8; 3];
254 let written = match self.engine.decode_slice(&quad, &mut decoded) {
255 Ok(written) => written,
256 Err(error) => {
257 wipe_bytes(&mut quad);
258 wipe_bytes(&mut decoded);
259 if consumed > 0 {
260 return Ok(consumed);
261 }
262 self.failed = true;
263 return Err(decode_io_error(error));
264 }
265 };
266
267 let result = self.output.push_slice(&decoded[..written]);
268 wipe_bytes(&mut quad);
269 wipe_bytes(&mut decoded);
270 if result.is_err() {
271 self.failed = true;
272 }
273 result?;
274 consumed += 4;
275
276 if written < 3 {
277 self.terminal_padding = true;
278 return Ok(consumed);
279 }
280 }
281
282 let tail = &input[consumed..];
283 self.pending[..tail.len()].copy_from_slice(tail);
284 self.pending_len = tail.len();
285 consumed += tail.len();
286 Ok(consumed)
287 }
288}
289
290impl<W, A, const PAD: bool> Drop for DecoderWriter<W, A, PAD>
291where
292 A: Alphabet,
293{
294 fn drop(&mut self) {
295 self.clear_pending();
296 self.clear_output();
297 }
298}
299
300impl<W, A, const PAD: bool> DecoderWriter<W, A, PAD>
301where
302 W: AsyncWrite + Unpin,
303 A: Alphabet + Unpin,
304{
305 fn poll_drain_output(&mut self, context: &mut Context<'_>) -> Poll<io::Result<()>> {
306 let mut chunk = [0u8; DECODE_OUTPUT_CAP];
307 while !self.output.is_empty() {
308 let pending = self.output.copy_front(&mut chunk);
309 let result = Pin::new(self.inner_mut()).poll_write(context, &chunk[..pending]);
310 wipe_bytes(&mut chunk[..pending]);
311 match result {
312 Poll::Pending => return Poll::Pending,
313 Poll::Ready(Ok(0)) => {
314 return Poll::Ready(Err(io::Error::new(
315 io::ErrorKind::WriteZero,
316 "base64-ng-tokio decoder writer could not drain buffered output",
317 )));
318 }
319 Poll::Ready(Ok(written)) => {
320 if written > pending {
321 self.failed = true;
322 return Poll::Ready(Err(io::Error::new(
323 io::ErrorKind::InvalidData,
324 "wrapped async writer reported more bytes than provided",
325 )));
326 }
327 self.output.discard_front(written);
328 }
329 Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
330 }
331 }
332
333 Poll::Ready(Ok(()))
334 }
335}
336
337impl<W, A, const PAD: bool> AsyncWrite for DecoderWriter<W, A, PAD>
338where
339 W: AsyncWrite + Unpin,
340 A: Alphabet + Unpin,
341{
342 fn poll_write(
343 mut self: Pin<&mut Self>,
344 context: &mut Context<'_>,
345 input: &[u8],
346 ) -> Poll<io::Result<usize>> {
347 if self.failed {
348 return Poll::Ready(Err(io::Error::other(
349 "base64-ng-tokio decoder writer is failed",
350 )));
351 }
352
353 ready!(self.poll_drain_output(context))?;
354 if self.finalized {
355 return Poll::Ready(Err(io::Error::new(
356 io::ErrorKind::InvalidInput,
357 "base64-ng-tokio decoder writer received input after shutdown",
358 )));
359 }
360
361 Poll::Ready(self.process_input(input))
362 }
363
364 fn poll_flush(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<io::Result<()>> {
365 if self.failed {
366 return Poll::Ready(Err(io::Error::other(
367 "base64-ng-tokio decoder writer is failed",
368 )));
369 }
370
371 ready!(self.poll_drain_output(context))?;
372 Pin::new(self.inner_mut()).poll_flush(context)
373 }
374
375 fn poll_shutdown(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<io::Result<()>> {
376 if self.failed {
377 return Poll::Ready(Err(io::Error::other(
378 "base64-ng-tokio decoder writer is failed",
379 )));
380 }
381
382 ready!(self.poll_drain_output(context))?;
383 if !self.finalized {
384 if let Err(error) = self.queue_pending_final() {
385 self.clear_output();
386 return Poll::Ready(Err(error));
387 }
388 self.finalized = true;
389 }
390 ready!(self.poll_drain_output(context))?;
391 ready!(Pin::new(self.inner_mut()).poll_flush(context))?;
392 Pin::new(self.inner_mut()).poll_shutdown(context)
393 }
394}