Skip to content

Commit b9f6e06

Browse files
cgwaltersallisonkarlitskaya
authored andcommitted
splitstream: Drop an unsafe
This one didn't have a `SAFETY` comment; looking through git log here I do see: > It no longer does internal buffer allocations when reading tar headers, saving > another ~10% off the total time to print a merged dumpfile. However, it looks like that was going from allocation-per-read to "read into a global buffer", which we're still doing here. I didn't measure performance here, but I think we have other performance worries and it's not worth carrying an `unsafe` for this. In very hot I/O paths one *can* measure the impact of zero-initializing, but I'm doubtful of that here. Just looking at the bigger picture here I think instead of a generic `R: Read` we should take a `BufReader` and then use `fill_buf` on it, which is intended for this use case. Anyways for now just drop the `unsafe`. Also add tests. Signed-off-by: Colin Walters <[email protected]>
1 parent 748a924 commit b9f6e06

File tree

1 file changed

+63
-6
lines changed

1 file changed

+63
-6
lines changed

src/splitstream.rs

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,13 @@ fn read_u64_le<R: Read>(reader: &mut R) -> Result<Option<usize>> {
198198
}
199199
}
200200

201+
/// Using the provided [`vec`] as a buffer, read exactly [`size`]
202+
/// bytes of content from [`reader`] into it. Any existing content
203+
/// in [`vec`] will be discarded; however its capacity will be reused,
204+
/// making this function suitable for use in loops.
201205
fn read_into_vec(reader: &mut impl Read, vec: &mut Vec<u8>, size: usize) -> Result<()> {
202-
unsafe {
203-
vec.truncate(0);
204-
vec.reserve(size);
205-
reader.read_exact(std::slice::from_raw_parts_mut(vec.as_mut_ptr(), size))?;
206-
vec.set_len(size);
207-
}
206+
vec.resize(size, 0u8);
207+
reader.read_exact(vec.as_mut_slice())?;
208208
Ok(())
209209
}
210210

@@ -391,3 +391,60 @@ impl<F: Read> Read for SplitStreamReader<F> {
391391
}
392392
}
393393
}
394+
395+
#[cfg(test)]
396+
mod tests {
397+
use super::*;
398+
use std::io::Cursor;
399+
400+
#[test]
401+
fn test_read_into_vec() -> Result<()> {
402+
// Test with an empty reader
403+
let mut reader = Cursor::new(vec![]);
404+
let mut vec = Vec::new();
405+
let result = read_into_vec(&mut reader, &mut vec, 0);
406+
assert!(result.is_ok());
407+
assert_eq!(vec.len(), 0);
408+
409+
// Test with a reader that has some data
410+
let mut reader = Cursor::new(vec![1, 2, 3, 4, 5]);
411+
let mut vec = Vec::new();
412+
let result = read_into_vec(&mut reader, &mut vec, 3);
413+
assert!(result.is_ok());
414+
assert_eq!(vec.len(), 3);
415+
assert_eq!(vec, vec![1, 2, 3]);
416+
417+
// Test reading more than the reader has
418+
let mut reader = Cursor::new(vec![1, 2, 3]);
419+
let mut vec = Vec::new();
420+
let result = read_into_vec(&mut reader, &mut vec, 5);
421+
assert!(result.is_err());
422+
423+
// Test reading exactly what the reader has
424+
let mut reader = Cursor::new(vec![1, 2, 3]);
425+
let mut vec = Vec::new();
426+
let result = read_into_vec(&mut reader, &mut vec, 3);
427+
assert!(result.is_ok());
428+
assert_eq!(vec.len(), 3);
429+
assert_eq!(vec, vec![1, 2, 3]);
430+
431+
// Test reading into a vector with existing capacity
432+
let mut reader = Cursor::new(vec![1, 2, 3, 4, 5]);
433+
let mut vec = Vec::with_capacity(10);
434+
let result = read_into_vec(&mut reader, &mut vec, 4);
435+
assert!(result.is_ok());
436+
assert_eq!(vec.len(), 4);
437+
assert_eq!(vec, vec![1, 2, 3, 4]);
438+
assert_eq!(vec.capacity(), 10);
439+
440+
// Test reading into a vector with existing data
441+
let mut reader = Cursor::new(vec![1, 2, 3]);
442+
let mut vec = vec![9, 9, 9];
443+
let result = read_into_vec(&mut reader, &mut vec, 2);
444+
assert!(result.is_ok());
445+
assert_eq!(vec.len(), 2);
446+
assert_eq!(vec, vec![1, 2]);
447+
448+
Ok(())
449+
}
450+
}

0 commit comments

Comments
 (0)