Skip to main content

base64_stream/
to_base64_reader.rs

1use std::{
2    fmt,
3    io::{self, ErrorKind, Read},
4};
5
6use base64::{
7    Engine,
8    engine::{GeneralPurpose, general_purpose::STANDARD},
9};
10
11/// Read any data and encode them to base64 data.
12pub struct ToBase64Reader<R: Read, const N: usize = 4096> {
13    inner:       R,
14    buf:         [u8; N],
15    buf_length:  usize,
16    buf_offset:  usize,
17    temp:        [u8; 3],
18    temp_length: usize,
19    engine:      &'static GeneralPurpose,
20}
21
22impl<R: Read, const N: usize> fmt::Debug for ToBase64Reader<R, N> {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        f.debug_struct("ToBase64Reader")
25            .field("buf_length", &self.buf_length)
26            .field("buf_offset", &self.buf_offset)
27            .field("temp", &&self.temp[..self.temp_length])
28            .field("temp_length", &self.temp_length)
29            .finish_non_exhaustive()
30    }
31}
32
33impl<R: Read> ToBase64Reader<R> {
34    #[inline]
35    pub fn new(reader: R) -> ToBase64Reader<R> {
36        Self::new2(reader)
37    }
38}
39
40impl<R: Read, const N: usize> ToBase64Reader<R, N> {
41    #[inline]
42    pub fn new2(reader: R) -> ToBase64Reader<R, N> {
43        const { assert!(N >= 4, "buffer size N must be at least 4") };
44        ToBase64Reader {
45            inner:       reader,
46            buf:         [0u8; N],
47            buf_length:  0,
48            buf_offset:  0,
49            temp:        [0; 3],
50            temp_length: 0,
51            engine:      &STANDARD,
52        }
53    }
54}
55
56impl<R: Read, const N: usize> ToBase64Reader<R, N> {
57    fn buf_left_shift(&mut self, distance: usize) {
58        debug_assert!(self.buf_length >= distance);
59
60        self.buf_offset += distance;
61        self.buf_length -= distance;
62
63        if self.buf_offset >= N - 4 {
64            self.buf.copy_within(self.buf_offset..self.buf_offset + self.buf_length, 0);
65
66            self.buf_offset = 0;
67        }
68    }
69
70    #[inline]
71    fn drain_temp<'a>(&mut self, buf: &'a mut [u8]) -> &'a mut [u8] {
72        debug_assert!(self.temp_length > 0);
73        debug_assert!(!buf.is_empty());
74
75        let drain_length = buf.len().min(self.temp_length);
76
77        buf[..drain_length].copy_from_slice(&self.temp[..drain_length]);
78
79        self.temp_length -= drain_length;
80        self.temp.copy_within(drain_length..drain_length + self.temp_length, 0);
81
82        &mut buf[drain_length..]
83    }
84
85    #[inline]
86    fn drain_block<'a>(&mut self, mut buf: &'a mut [u8]) -> &'a mut [u8] {
87        debug_assert!(self.buf_length > 0);
88        debug_assert!(self.temp_length == 0);
89        debug_assert!(!buf.is_empty());
90
91        let drain_length = self.buf_length.min(3);
92
93        let mut b = [0; 4];
94
95        let encode_length = self
96            .engine
97            .encode_slice(&self.buf[self.buf_offset..(self.buf_offset + drain_length)], &mut b)
98            .unwrap();
99
100        self.buf_left_shift(drain_length);
101
102        let buf_length = buf.len();
103
104        if buf_length >= encode_length {
105            buf[..encode_length].copy_from_slice(&b[..encode_length]);
106
107            buf = &mut buf[encode_length..];
108        } else {
109            buf[..buf_length].copy_from_slice(&b[..buf_length]);
110
111            buf = &mut buf[buf_length..];
112
113            self.temp_length = encode_length - buf_length;
114            self.temp[..self.temp_length].copy_from_slice(&b[buf_length..encode_length]);
115        }
116
117        buf
118    }
119
120    fn drain<'a>(&mut self, mut buf: &'a mut [u8]) -> &'a mut [u8] {
121        if buf.is_empty() {
122            return buf;
123        }
124
125        if self.temp_length > 0 {
126            buf = self.drain_temp(buf);
127        }
128
129        debug_assert!(self.buf_length >= 3);
130
131        let buf_length = buf.len();
132
133        if buf_length >= 4 {
134            debug_assert!(self.temp_length == 0);
135
136            let actual_max_read_size = (buf_length >> 2) * 3; // (buf_length / 4) * 3
137            let max_available_self_buf_length = self.buf_length - (self.buf_length % 3);
138
139            let drain_length = max_available_self_buf_length.min(actual_max_read_size);
140
141            let encode_length = self
142                .engine
143                .encode_slice(&self.buf[self.buf_offset..(self.buf_offset + drain_length)], buf)
144                .unwrap();
145
146            buf = &mut buf[encode_length..];
147
148            self.buf_left_shift(drain_length);
149        }
150
151        if !buf.is_empty() && self.buf_length >= 3 { self.drain_block(buf) } else { buf }
152    }
153
154    #[inline]
155    fn drain_end<'a>(&mut self, mut buf: &'a mut [u8]) -> &'a mut [u8] {
156        if buf.is_empty() {
157            return buf;
158        }
159
160        if self.temp_length > 0 {
161            buf = self.drain_temp(buf);
162        }
163
164        if !buf.is_empty() && self.buf_length > 0 { self.drain_block(buf) } else { buf }
165    }
166
167    /// Returns the inner reader, consuming this wrapper.
168    #[inline]
169    pub fn into_inner(self) -> R {
170        self.inner
171    }
172}
173
174impl<R: Read, const N: usize> Read for ToBase64Reader<R, N> {
175    fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, io::Error> {
176        let original_buf_length = buf.len();
177
178        while self.buf_length < 3 {
179            match self.inner.read(&mut self.buf[(self.buf_offset + self.buf_length)..]) {
180                Ok(0) => {
181                    buf = self.drain_end(buf);
182
183                    return Ok(original_buf_length - buf.len());
184                },
185                Ok(c) => self.buf_length += c,
186                Err(ref e) if e.kind() == ErrorKind::Interrupted => {},
187                Err(e) => return Err(e),
188            }
189        }
190
191        buf = self.drain(buf);
192
193        Ok(original_buf_length - buf.len())
194    }
195}
196
197impl<R: Read> From<R> for ToBase64Reader<R> {
198    #[inline]
199    fn from(reader: R) -> Self {
200        ToBase64Reader::new(reader)
201    }
202}