Skip to main content

ipld_car/bounded_reader/
functions.rs

1use crate::bounded_reader::traits::{Bounded, BoundedIndex, CloneAndRewind};
2use std::{io::Read, ops::RangeInclusive};
3
4pub fn slice_ref<R>(reader: R, pattern: &[u8]) -> Option<R>
5where
6	R: Read + CloneAndRewind + Bounded,
7	RangeInclusive<u64>: BoundedIndex<R>,
8{
9	if pattern.is_empty() {
10		return None;
11	}
12
13	const BUF_SIZE: usize = 4096;
14	let mut buf = [0u8; BUF_SIZE];
15
16	let last_reader_pos = reader.bound_len().saturating_sub(pattern.len() as u64 - 1);
17	let mut reader_pos = 0u64;
18	let mut pattern_pos = 0usize;
19
20	let mut src = reader.clone_and_rewind();
21	loop {
22		let n = match src.read(&mut buf) {
23			Ok(0) | Err(_) => break,
24			Ok(n) => n,
25		};
26		for &byte in &buf[..n] {
27			if reader_pos > last_reader_pos && pattern_pos == 0 {
28				return None;
29			}
30
31			if byte == pattern[pattern_pos] {
32				pattern_pos += 1;
33				if pattern_pos >= pattern.len() {
34					let start = reader_pos.saturating_sub(pattern.len() as u64 - 1);
35					return Some(reader.clamped_sub(start..=reader_pos));
36				}
37			} else {
38				pattern_pos = 0;
39			}
40
41			reader_pos += 1;
42		}
43	}
44
45	None
46}
47
48#[cfg(test)]
49mod tests {
50	use super::*;
51	use crate::bounded_reader::sync::BoundedReader;
52
53	use anyhow::Result;
54	use std::io::{self, Cursor};
55	use test_case::case;
56
57	#[case( b"Hello world!!!", b"world", Some(b"world"); "middle")]
58	#[case( b"Hello world!!!", b"Hello", Some(b"Hello"); "begin")]
59	#[case( b"Hello world!!!", b"ld!!!", Some(b"ld!!!"); "end")]
60	#[case( b"Hello world!!!", b"universe", None; "none")]
61	fn slice_ref_test(data: &[u8], pattern: &[u8], exp_slice: Option<&[u8]>) -> Result<()> {
62		let reader = BoundedReader::from_reader(Cursor::new(data))?;
63		let slice = slice_ref(reader, pattern)
64			.map(|mut sliced_reader| {
65				let mut content = Vec::<u8>::with_capacity(sliced_reader.bound_len() as usize);
66				sliced_reader.read_to_end(&mut content)?;
67				Ok::<_, io::Error>(content)
68			})
69			.transpose()?;
70
71		assert_eq!(slice.as_deref(), exp_slice);
72		Ok(())
73	}
74}