1use alloc::{string::String, vec::Vec};
4
5use super::{
6 contracts::BackendFault,
7 ordinary::OneShotError,
8 specifications::{Base64, Codec},
9};
10
11impl<S: Codec> Base64<S> {
12 pub fn encode_append(
19 &self,
20 input: &[u8],
21 destination: &mut String,
22 ) -> Result<usize, OneShotError> {
23 self.encode_append_inner(
24 input,
25 destination,
26 |output, required| {
27 output
28 .try_reserve_exact(required)
29 .map_err(|_| OneShotError::AllocationFailed {
30 requested: required,
31 })
32 },
33 |_, _| Ok(()),
34 )
35 }
36
37 pub fn decode_append(
43 &self,
44 input: &[u8],
45 destination: &mut Vec<u8>,
46 ) -> Result<usize, OneShotError> {
47 self.decode_append_inner(
48 input,
49 destination,
50 |output, required| {
51 output
52 .try_reserve_exact(required)
53 .map_err(|_| OneShotError::AllocationFailed {
54 requested: required,
55 })
56 },
57 |_| Ok(()),
58 )
59 }
60
61 fn encode_append_inner<R, H>(
62 &self,
63 input: &[u8],
64 destination: &mut String,
65 reserve: R,
66 mut after_chunk: H,
67 ) -> Result<usize, OneShotError>
68 where
69 R: FnOnce(&mut String, usize) -> Result<(), OneShotError>,
70 H: FnMut(&mut String, usize) -> Result<(), OneShotError>,
71 {
72 let required = self.encoded_len(input.len())?;
73 destination
74 .len()
75 .checked_add(required)
76 .ok_or(OneShotError::LengthOverflow)?;
77 reserve(destination, required)?;
78
79 let mut rollback = StringRollback::new(destination);
80 for chunk in self.encoded_chunks(input)? {
81 let text = chunk
82 .as_str()
83 .map_err(|_| OneShotError::Backend(BackendFault::ImpossibleState))?;
84 rollback.destination().push_str(text);
85 after_chunk(rollback.destination(), text.len())?;
86 }
87 rollback.commit();
88 Ok(required)
89 }
90
91 fn decode_append_inner<R, H>(
92 &self,
93 input: &[u8],
94 destination: &mut Vec<u8>,
95 reserve: R,
96 after_decode: H,
97 ) -> Result<usize, OneShotError>
98 where
99 R: FnOnce(&mut Vec<u8>, usize) -> Result<(), OneShotError>,
100 H: FnOnce(&mut Vec<u8>) -> Result<(), OneShotError>,
101 {
102 let required = self.decoded_len(input)?;
103 let original_len = destination.len();
104 let total = original_len
105 .checked_add(required)
106 .ok_or(OneShotError::LengthOverflow)?;
107 reserve(destination, required)?;
108
109 let mut rollback = VecRollback::new(destination);
110 rollback.destination().resize(total, 0);
111 self.decode_into(input, &mut rollback.destination()[original_len..total])?;
112 after_decode(rollback.destination())?;
113 rollback.commit();
114 Ok(required)
115 }
116
117 #[cfg(test)]
118 pub(super) fn encode_append_with_hooks<R, H>(
119 &self,
120 input: &[u8],
121 destination: &mut String,
122 reserve: R,
123 after_chunk: H,
124 ) -> Result<usize, OneShotError>
125 where
126 R: FnOnce(&mut String, usize) -> Result<(), OneShotError>,
127 H: FnMut(&mut String, usize) -> Result<(), OneShotError>,
128 {
129 self.encode_append_inner(input, destination, reserve, after_chunk)
130 }
131
132 #[cfg(test)]
133 pub(super) fn decode_append_with_hooks<R, H>(
134 &self,
135 input: &[u8],
136 destination: &mut Vec<u8>,
137 reserve: R,
138 after_decode: H,
139 ) -> Result<usize, OneShotError>
140 where
141 R: FnOnce(&mut Vec<u8>, usize) -> Result<(), OneShotError>,
142 H: FnOnce(&mut Vec<u8>) -> Result<(), OneShotError>,
143 {
144 self.decode_append_inner(input, destination, reserve, after_decode)
145 }
146}
147
148struct StringRollback<'a> {
149 destination: &'a mut String,
150 original_len: usize,
151 committed: bool,
152}
153
154impl<'a> StringRollback<'a> {
155 fn new(destination: &'a mut String) -> Self {
156 let original_len = destination.len();
157 Self {
158 destination,
159 original_len,
160 committed: false,
161 }
162 }
163
164 fn destination(&mut self) -> &mut String {
165 self.destination
166 }
167
168 fn commit(mut self) {
169 self.committed = true;
170 }
171}
172
173impl Drop for StringRollback<'_> {
174 fn drop(&mut self) {
175 if !self.committed {
176 self.destination.truncate(self.original_len);
177 }
178 }
179}
180
181struct VecRollback<'a> {
182 destination: &'a mut Vec<u8>,
183 original_len: usize,
184 committed: bool,
185}
186
187impl<'a> VecRollback<'a> {
188 fn new(destination: &'a mut Vec<u8>) -> Self {
189 let original_len = destination.len();
190 Self {
191 destination,
192 original_len,
193 committed: false,
194 }
195 }
196
197 fn destination(&mut self) -> &mut Vec<u8> {
198 self.destination
199 }
200
201 fn commit(mut self) {
202 self.committed = true;
203 }
204}
205
206impl Drop for VecRollback<'_> {
207 fn drop(&mut self) {
208 if !self.committed {
209 self.destination.truncate(self.original_len);
210 }
211 }
212}