1use std::io::Read;
15use std::io::Write;
16use std::io::{self};
17
18use crate::ExtractionError;
19
20const COPY_BUFFER_SIZE: usize = 64 * 1024;
25
26#[derive(Debug)]
49pub struct CopyBuffer {
50 #[allow(clippy::large_stack_arrays)]
52 buf: [u8; COPY_BUFFER_SIZE],
53}
54
55impl CopyBuffer {
56 #[inline]
60 #[must_use]
61 #[allow(clippy::large_stack_arrays)]
62 pub fn new() -> Self {
63 Self {
64 buf: [0u8; COPY_BUFFER_SIZE],
65 }
66 }
67
68 #[inline]
70 #[must_use]
71 pub fn size(&self) -> usize {
72 COPY_BUFFER_SIZE
73 }
74}
75
76impl Default for CopyBuffer {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82#[inline]
118pub fn copy_with_buffer<R: Read, W: Write>(
119 reader: &mut R,
120 writer: &mut W,
121 buffer: &mut CopyBuffer,
122) -> Result<u64, ExtractionError> {
123 let mut total: u64 = 0;
124
125 loop {
126 let bytes_read = match reader.read(&mut buffer.buf) {
127 Ok(0) => break,
128 Ok(n) => n,
129 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
130 Err(e) => return Err(ExtractionError::Io(e)),
131 };
132
133 writer
134 .write_all(&buffer.buf[..bytes_read])
135 .map_err(ExtractionError::Io)?;
136
137 total = total
139 .checked_add(bytes_read as u64)
140 .ok_or(ExtractionError::QuotaExceeded {
141 resource: crate::QuotaResource::IntegerOverflow,
142 })?;
143 }
144
145 Ok(total)
146}
147
148#[cfg(test)]
149#[allow(clippy::unwrap_used)]
150mod tests {
151 use super::*;
152 use std::io::Cursor;
153
154 #[test]
155 fn test_copy_buffer_new() {
156 let buffer = CopyBuffer::new();
157 assert_eq!(buffer.size(), 64 * 1024);
158 }
159
160 #[test]
161 fn test_copy_buffer_default() {
162 let buffer = CopyBuffer::default();
163 assert_eq!(buffer.size(), 64 * 1024);
164 }
165
166 #[test]
167 fn test_copy_empty_source() {
168 let mut buffer = CopyBuffer::new();
169 let mut input = Cursor::new(Vec::<u8>::new());
170 let mut output = Vec::new();
171
172 let result = copy_with_buffer(&mut input, &mut output, &mut buffer);
173 assert!(result.is_ok());
174 assert_eq!(result.unwrap(), 0);
175 assert_eq!(output.len(), 0);
176 }
177
178 #[test]
179 fn test_copy_small_data() {
180 let mut buffer = CopyBuffer::new();
181 let input_data = b"Hello, World!";
182 let mut input = Cursor::new(input_data);
183 let mut output = Vec::new();
184
185 let result = copy_with_buffer(&mut input, &mut output, &mut buffer);
186 assert!(result.is_ok());
187 assert_eq!(result.unwrap(), input_data.len() as u64);
188 assert_eq!(output, input_data);
189 }
190
191 #[test]
192 fn test_copy_large_data() {
193 let mut buffer = CopyBuffer::new();
194 let input_data = vec![0x42u8; 1024 * 1024];
196 let mut input = Cursor::new(&input_data);
197 let mut output = Vec::new();
198
199 let result = copy_with_buffer(&mut input, &mut output, &mut buffer);
200 assert!(result.is_ok());
201 assert_eq!(result.unwrap(), input_data.len() as u64);
202 assert_eq!(output, input_data);
203 }
204
205 #[test]
206 fn test_copy_exact_buffer_size() {
207 let mut buffer = CopyBuffer::new();
208 let input_data = vec![0xAAu8; COPY_BUFFER_SIZE];
209 let mut input = Cursor::new(&input_data);
210 let mut output = Vec::new();
211
212 let result = copy_with_buffer(&mut input, &mut output, &mut buffer);
213 assert!(result.is_ok());
214 assert_eq!(result.unwrap(), COPY_BUFFER_SIZE as u64);
215 assert_eq!(output, input_data);
216 }
217
218 #[test]
219 fn test_copy_multiple_chunks() {
220 let mut buffer = CopyBuffer::new();
221 let input_data = vec![0x55u8; COPY_BUFFER_SIZE * 3 + 1000];
223 let mut input = Cursor::new(&input_data);
224 let mut output = Vec::new();
225
226 let result = copy_with_buffer(&mut input, &mut output, &mut buffer);
227 assert!(result.is_ok());
228 assert_eq!(result.unwrap(), input_data.len() as u64);
229 assert_eq!(output, input_data);
230 }
231
232 #[test]
233 fn test_copy_reusable_buffer() {
234 let mut buffer = CopyBuffer::new();
235
236 let data1 = b"First copy";
238 let mut input1 = Cursor::new(data1);
239 let mut output1 = Vec::new();
240 let result1 = copy_with_buffer(&mut input1, &mut output1, &mut buffer);
241 assert!(result1.is_ok());
242 assert_eq!(output1, data1);
243
244 let data2 = b"Second copy with different data";
246 let mut input2 = Cursor::new(data2);
247 let mut output2 = Vec::new();
248 let result2 = copy_with_buffer(&mut input2, &mut output2, &mut buffer);
249 assert!(result2.is_ok());
250 assert_eq!(output2, data2);
251 }
252
253 #[test]
254 fn test_copy_byte_for_byte_correctness() {
255 let mut buffer = CopyBuffer::new();
256 let mut input_data = Vec::new();
258 for i in 0..=255u8 {
259 input_data.extend_from_slice(&[i; 256]);
260 }
261
262 let mut input = Cursor::new(&input_data);
263 let mut output = Vec::new();
264
265 let result = copy_with_buffer(&mut input, &mut output, &mut buffer);
266 assert!(result.is_ok());
267 assert_eq!(result.unwrap(), input_data.len() as u64);
268 assert_eq!(output, input_data);
269 }
270
271 #[test]
273 fn test_copy_with_interrupted_reads() {
274 use std::io::Error;
275 use std::io::ErrorKind;
276
277 struct InterruptedReader {
279 data: Vec<u8>,
280 position: usize,
281 interrupt_count: usize,
282 }
283
284 impl Read for InterruptedReader {
285 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
286 if self.interrupt_count.is_multiple_of(3) && self.position < self.data.len() {
287 self.interrupt_count += 1;
288 return Err(Error::new(ErrorKind::Interrupted, "interrupted"));
289 }
290
291 self.interrupt_count += 1;
292
293 if self.position >= self.data.len() {
294 return Ok(0); }
296
297 let remaining = self.data.len() - self.position;
298 let to_read = remaining.min(buf.len());
299 buf[..to_read].copy_from_slice(&self.data[self.position..self.position + to_read]);
300 self.position += to_read;
301 Ok(to_read)
302 }
303 }
304
305 let test_data = vec![0x42u8; 1000];
306 let mut reader = InterruptedReader {
307 data: test_data.clone(),
308 position: 0,
309 interrupt_count: 0,
310 };
311
312 let mut buffer = CopyBuffer::new();
313 let mut output = Vec::new();
314
315 let result = copy_with_buffer(&mut reader, &mut output, &mut buffer);
316 assert!(result.is_ok(), "copy should handle interrupted reads");
317 assert_eq!(
318 output, test_data,
319 "data should be copied correctly despite interruptions"
320 );
321 }
322
323 #[test]
325 fn test_copy_with_write_failure() {
326 use std::io::Error;
327 use std::io::ErrorKind;
328
329 struct FailingWriter {
331 written: usize,
332 fail_after: usize,
333 }
334
335 impl Write for FailingWriter {
336 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
337 if self.written >= self.fail_after {
338 return Err(Error::other("write failed"));
339 }
340 let to_write = (self.fail_after - self.written).min(buf.len());
341 self.written += to_write;
342 Ok(to_write)
343 }
344
345 fn flush(&mut self) -> std::io::Result<()> {
346 Ok(())
347 }
348 }
349
350 let test_data = vec![0x42u8; 1000];
351 let mut input = Cursor::new(test_data);
352 let mut writer = FailingWriter {
353 written: 0,
354 fail_after: 500,
355 };
356
357 let mut buffer = CopyBuffer::new();
358 let result = copy_with_buffer(&mut input, &mut writer, &mut buffer);
359
360 assert!(result.is_err(), "copy should propagate write errors");
361 match result {
362 Err(ExtractionError::Io(e)) => {
363 assert_eq!(e.kind(), ErrorKind::Other);
364 }
365 _ => panic!("expected IO error"),
366 }
367 }
368}