#![allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct ReadStats {
pub total_reads: u64,
pub total_bytes: u64,
pub cache_hits: u64,
pub avg_read_size: f64,
}
#[derive(Debug)]
pub struct BufferedMediaReader {
data: Vec<u8>,
position: usize,
pub buffer_size: usize,
read_count: u64,
bytes_read: u64,
peek_count: u64,
}
impl BufferedMediaReader {
#[must_use]
pub fn from_bytes(data: Vec<u8>) -> Self {
let buffer_size = data.len().max(65536);
Self {
data,
position: 0,
buffer_size,
read_count: 0,
bytes_read: 0,
peek_count: 0,
}
}
#[must_use]
pub fn from_bytes_with_buffer_size(data: Vec<u8>, buffer_size: usize) -> Self {
Self {
buffer_size,
..Self::from_bytes(data)
}
}
#[must_use]
pub fn position(&self) -> usize {
self.position
}
#[must_use]
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.position)
}
#[must_use]
pub fn is_eof(&self) -> bool {
self.position >= self.data.len()
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn seek(&mut self, pos: usize) -> bool {
if pos > self.data.len() {
return false;
}
self.position = pos;
true
}
pub fn skip(&mut self, count: usize) -> usize {
let available = self.remaining();
let actual = count.min(available);
self.position += actual;
actual
}
pub fn read(&mut self, count: usize) -> &[u8] {
let start = self.position;
let end = (start + count).min(self.data.len());
let actual = end - start;
self.position = end;
self.read_count += 1;
self.bytes_read += actual as u64;
&self.data[start..end]
}
pub fn peek(&self, count: usize) -> &[u8] {
let start = self.position;
let end = (start + count).min(self.data.len());
&self.data[start..end]
}
pub fn read_exact(&mut self, count: usize) -> Option<Vec<u8>> {
if self.remaining() < count {
return None;
}
let start = self.position;
let end = start + count;
self.position = end;
self.read_count += 1;
self.bytes_read += count as u64;
Some(self.data[start..end].to_vec())
}
pub fn read_u8(&mut self) -> Option<u8> {
if self.is_eof() {
return None;
}
let val = self.data[self.position];
self.position += 1;
self.read_count += 1;
self.bytes_read += 1;
Some(val)
}
pub fn read_u16_be(&mut self) -> Option<u16> {
let bytes = self.read_exact(2)?;
Some(u16::from_be_bytes([bytes[0], bytes[1]]))
}
pub fn read_u32_be(&mut self) -> Option<u32> {
let bytes = self.read_exact(4)?;
Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
pub fn read_u64_be(&mut self) -> Option<u64> {
let bytes = self.read_exact(8)?;
Some(u64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]))
}
pub fn read_u16_le(&mut self) -> Option<u16> {
let bytes = self.read_exact(2)?;
Some(u16::from_le_bytes([bytes[0], bytes[1]]))
}
pub fn read_u32_le(&mut self) -> Option<u32> {
let bytes = self.read_exact(4)?;
Some(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
#[must_use]
pub fn find_pattern(&self, pattern: &[u8]) -> Option<usize> {
if pattern.is_empty() {
return Some(self.position);
}
let haystack = &self.data[self.position..];
bmh_search(haystack, pattern).map(|rel| rel + self.position)
}
#[must_use]
pub fn stats(&self) -> ReadStats {
let avg_read_size = if self.read_count == 0 {
0.0
} else {
#[allow(clippy::cast_precision_loss)]
{
self.bytes_read as f64 / self.read_count as f64
}
};
ReadStats {
total_reads: self.read_count,
total_bytes: self.bytes_read,
cache_hits: self.peek_count,
avg_read_size,
}
}
}
#[derive(Debug, Clone)]
pub struct ReadAheadConfig {
pub min_window: usize,
pub max_window: usize,
pub initial_window: usize,
pub growth_factor_per_mille: u32,
pub shrink_factor_per_mille: u32,
pub sequential_threshold: usize,
}
impl Default for ReadAheadConfig {
fn default() -> Self {
Self {
min_window: 4096,
max_window: 4 * 1024 * 1024, initial_window: 65536, growth_factor_per_mille: 1500, shrink_factor_per_mille: 500, sequential_threshold: 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessPattern {
Sequential,
Random,
Unknown,
}
#[derive(Debug, Clone, Default)]
pub struct ReadAheadStats {
pub total_reads: u64,
pub cache_hits: u64,
pub cache_misses: u64,
pub current_window: usize,
pub pattern: Option<AccessPattern>,
pub total_bytes_delivered: u64,
}
pub struct AdaptiveReadAheadReader {
data: Vec<u8>,
position: usize,
buffer: Vec<u8>,
buffer_start: usize,
config: ReadAheadConfig,
current_window: usize,
sequential_count: usize,
last_read_end: Option<usize>,
stats: ReadAheadStats,
}
impl AdaptiveReadAheadReader {
#[must_use]
pub fn new(data: Vec<u8>) -> Self {
Self::with_config(data, ReadAheadConfig::default())
}
#[must_use]
pub fn with_config(data: Vec<u8>, config: ReadAheadConfig) -> Self {
let initial_window = config
.initial_window
.clamp(config.min_window, config.max_window);
Self {
data,
position: 0,
buffer: Vec::new(),
buffer_start: 0,
current_window: initial_window,
config,
sequential_count: 0,
last_read_end: None,
stats: ReadAheadStats::default(),
}
}
#[must_use]
pub fn position(&self) -> usize {
self.position
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[must_use]
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.position)
}
#[must_use]
pub fn is_eof(&self) -> bool {
self.position >= self.data.len()
}
#[must_use]
pub fn current_window(&self) -> usize {
self.current_window
}
#[must_use]
pub fn access_pattern(&self) -> AccessPattern {
self.stats.pattern.unwrap_or(AccessPattern::Unknown)
}
#[must_use]
pub fn stats(&self) -> ReadAheadStats {
ReadAheadStats {
current_window: self.current_window,
pattern: Some(self.access_pattern()),
..self.stats.clone()
}
}
pub fn seek(&mut self, pos: usize) -> bool {
if pos > self.data.len() {
return false;
}
self.position = pos;
true
}
pub fn read(&mut self, count: usize) -> Vec<u8> {
if self.is_eof() || count == 0 {
self.stats.total_reads += 1;
return Vec::new();
}
self.update_pattern();
let actual_count = count.min(self.remaining());
if self.is_in_buffer(self.position, actual_count) {
self.stats.cache_hits += 1;
} else {
self.stats.cache_misses += 1;
self.fill_buffer();
}
let start = self.position;
let end = (start + actual_count).min(self.data.len());
let result = self.data[start..end].to_vec();
let bytes_read = result.len();
self.position = end;
self.last_read_end = Some(end);
self.stats.total_reads += 1;
self.stats.total_bytes_delivered += bytes_read as u64;
result
}
pub fn read_exact(&mut self, count: usize) -> Option<Vec<u8>> {
if self.remaining() < count {
return None;
}
Some(self.read(count))
}
#[must_use]
pub fn peek(&self, count: usize) -> &[u8] {
let start = self.position;
let end = (start + count).min(self.data.len());
&self.data[start..end]
}
fn is_in_buffer(&self, offset: usize, len: usize) -> bool {
if self.buffer.is_empty() {
return false;
}
let buffer_end = self.buffer_start + self.buffer.len();
offset >= self.buffer_start && offset + len <= buffer_end
}
fn fill_buffer(&mut self) {
let start = self.position;
let window_end = (start + self.current_window).min(self.data.len());
self.buffer = self.data[start..window_end].to_vec();
self.buffer_start = start;
}
#[allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss
)]
fn update_pattern(&mut self) {
let is_sequential = match self.last_read_end {
Some(last_end) => {
let diff = if self.position >= last_end {
self.position - last_end
} else {
usize::MAX
};
diff <= 64
}
None => true, };
if is_sequential {
self.sequential_count = self.sequential_count.saturating_add(1);
if self.sequential_count >= self.config.sequential_threshold {
self.stats.pattern = Some(AccessPattern::Sequential);
let new_window = (self.current_window as f64
* (self.config.growth_factor_per_mille as f64 / 1000.0))
as usize;
self.current_window =
new_window.clamp(self.config.min_window, self.config.max_window);
}
} else {
self.sequential_count = 0;
self.stats.pattern = Some(AccessPattern::Random);
let new_window = (self.current_window as f64
* (self.config.shrink_factor_per_mille as f64 / 1000.0))
as usize;
self.current_window = new_window.clamp(self.config.min_window, self.config.max_window);
}
}
}
fn bmh_search(haystack: &[u8], needle: &[u8]) -> Option<usize> {
let n = haystack.len();
let m = needle.len();
if m == 0 {
return Some(0);
}
if m > n {
return None;
}
let mut skip = [m; 256];
for (i, &b) in needle.iter().enumerate().take(m - 1) {
skip[b as usize] = m - 1 - i;
}
let mut i = m - 1;
while i < n {
let mut k = 0usize;
let mut j = m - 1;
loop {
if haystack[i - k] != needle[j] {
break;
}
if j == 0 {
return Some(i - (m - 1));
}
k += 1;
j -= 1;
}
i += skip[haystack[i] as usize];
}
None
}
#[cfg(test)]
mod tests {
use super::*;
fn make_reader(data: &[u8]) -> BufferedMediaReader {
BufferedMediaReader::from_bytes(data.to_vec())
}
#[test]
fn test_initial_state() {
let r = make_reader(b"hello");
assert_eq!(r.position(), 0);
assert_eq!(r.remaining(), 5);
assert!(!r.is_eof());
}
#[test]
fn test_empty_reader() {
let r = make_reader(b"");
assert!(r.is_eof());
assert_eq!(r.remaining(), 0);
}
#[test]
fn test_read_advances_position() {
let mut r = make_reader(b"abcdef");
let slice = r.read(3);
assert_eq!(slice, b"abc");
assert_eq!(r.position(), 3);
assert_eq!(r.remaining(), 3);
}
#[test]
fn test_read_past_end_clips() {
let mut r = make_reader(b"ab");
let slice = r.read(100);
assert_eq!(slice, b"ab");
assert!(r.is_eof());
}
#[test]
fn test_peek_does_not_advance() {
let r = make_reader(b"abcdef");
let s = r.peek(3);
assert_eq!(s, b"abc");
assert_eq!(r.position(), 0); }
#[test]
fn test_peek_past_end_clips() {
let r = make_reader(b"xy");
assert_eq!(r.peek(100), b"xy");
}
#[test]
fn test_seek_valid() {
let mut r = make_reader(b"abcdef");
assert!(r.seek(3));
assert_eq!(r.position(), 3);
}
#[test]
fn test_seek_past_end_fails() {
let mut r = make_reader(b"abc");
assert!(!r.seek(10));
assert_eq!(r.position(), 0);
}
#[test]
fn test_skip_partial() {
let mut r = make_reader(b"abcde");
let skipped = r.skip(3);
assert_eq!(skipped, 3);
assert_eq!(r.position(), 3);
}
#[test]
fn test_skip_past_end_clips() {
let mut r = make_reader(b"ab");
let skipped = r.skip(100);
assert_eq!(skipped, 2);
assert!(r.is_eof());
}
#[test]
fn test_read_exact_success() {
let mut r = make_reader(b"abcdef");
let v = r.read_exact(4).expect("should succeed");
assert_eq!(v, b"abcd");
assert_eq!(r.position(), 4);
}
#[test]
fn test_read_exact_insufficient() {
let mut r = make_reader(b"ab");
assert!(r.read_exact(5).is_none());
}
#[test]
fn test_read_u8() {
let mut r = make_reader(&[0x42]);
assert_eq!(r.read_u8(), Some(0x42));
assert!(r.read_u8().is_none());
}
#[test]
fn test_read_u16_be() {
let mut r = make_reader(&[0x01, 0x02]);
assert_eq!(r.read_u16_be(), Some(0x0102));
}
#[test]
fn test_read_u16_le() {
let mut r = make_reader(&[0x01, 0x02]);
assert_eq!(r.read_u16_le(), Some(0x0201));
}
#[test]
fn test_read_u32_be() {
let mut r = make_reader(&[0x00, 0x01, 0x02, 0x03]);
assert_eq!(r.read_u32_be(), Some(0x0001_0203));
}
#[test]
fn test_read_u32_le() {
let mut r = make_reader(&[0x01, 0x00, 0x00, 0x00]);
assert_eq!(r.read_u32_le(), Some(1));
}
#[test]
fn test_read_u64_be() {
let data = [0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF];
let mut r = make_reader(&data);
assert_eq!(r.read_u64_be(), Some(0xFF));
}
#[test]
fn test_find_pattern_found() {
let r = make_reader(b"the quick brown fox");
let pos = r.find_pattern(b"brown");
assert_eq!(pos, Some(10));
}
#[test]
fn test_find_pattern_not_found() {
let r = make_reader(b"hello world");
assert_eq!(r.find_pattern(b"xyz"), None);
}
#[test]
fn test_find_pattern_from_current_position() {
let mut r = make_reader(b"aababc");
r.seek(2); let pos = r.find_pattern(b"ab");
assert!(pos.is_some());
assert!(pos.expect("pattern should be found after seek") >= 2);
}
#[test]
fn test_find_empty_pattern() {
let r = make_reader(b"abc");
assert_eq!(r.find_pattern(b""), Some(0));
}
#[test]
fn test_stats_after_reads() {
let mut r = make_reader(b"0123456789");
r.read(4);
r.read(3);
let s = r.stats();
assert_eq!(s.total_reads, 2);
assert_eq!(s.total_bytes, 7);
assert!((s.avg_read_size - 3.5).abs() < 1e-9);
}
#[test]
fn test_stats_empty() {
let r = make_reader(b"abc");
let s = r.stats();
assert_eq!(s.total_reads, 0);
assert_eq!(s.avg_read_size, 0.0);
}
#[test]
fn test_adaptive_initial_state() {
let data = vec![0u8; 1024];
let reader = AdaptiveReadAheadReader::new(data);
assert_eq!(reader.position(), 0);
assert_eq!(reader.len(), 1024);
assert!(!reader.is_eof());
assert_eq!(reader.remaining(), 1024);
assert_eq!(reader.access_pattern(), AccessPattern::Unknown);
}
#[test]
fn test_adaptive_read_basic() {
let data: Vec<u8> = (0..100).collect();
let mut reader = AdaptiveReadAheadReader::new(data);
let chunk = reader.read(10);
assert_eq!(chunk.len(), 10);
assert_eq!(chunk, (0..10).collect::<Vec<u8>>());
assert_eq!(reader.position(), 10);
}
#[test]
fn test_adaptive_read_exact() {
let data: Vec<u8> = (0..50).collect();
let mut reader = AdaptiveReadAheadReader::new(data);
let chunk = reader.read_exact(10);
assert!(chunk.is_some());
assert_eq!(chunk.as_ref().map(|c| c.len()), Some(10));
assert!(reader.read_exact(100).is_none());
}
#[test]
fn test_adaptive_sequential_detection() {
let data = vec![0u8; 65536];
let config = ReadAheadConfig {
sequential_threshold: 3,
..Default::default()
};
let mut reader = AdaptiveReadAheadReader::with_config(data, config);
for _ in 0..5 {
reader.read(100);
}
assert_eq!(reader.access_pattern(), AccessPattern::Sequential);
}
#[test]
fn test_adaptive_random_detection() {
let data = vec![0u8; 65536];
let config = ReadAheadConfig {
sequential_threshold: 2,
..Default::default()
};
let mut reader = AdaptiveReadAheadReader::with_config(data, config);
reader.read(100);
reader.read(100);
reader.read(100);
reader.seek(0);
reader.read(50);
assert_eq!(reader.access_pattern(), AccessPattern::Random);
}
#[test]
fn test_adaptive_window_grows_on_sequential() {
let data = vec![0u8; 1024 * 1024]; let config = ReadAheadConfig {
min_window: 1024,
max_window: 1024 * 1024,
initial_window: 4096,
growth_factor_per_mille: 2000, shrink_factor_per_mille: 500,
sequential_threshold: 2,
};
let initial = config.initial_window;
let mut reader = AdaptiveReadAheadReader::with_config(data, config);
for _ in 0..5 {
reader.read(100);
}
assert!(reader.current_window() > initial);
}
#[test]
fn test_adaptive_window_shrinks_on_random() {
let data = vec![0u8; 1024 * 1024];
let config = ReadAheadConfig {
min_window: 1024,
max_window: 1024 * 1024,
initial_window: 65536,
growth_factor_per_mille: 1500,
shrink_factor_per_mille: 500, sequential_threshold: 2,
};
let mut reader = AdaptiveReadAheadReader::with_config(data, config);
for _ in 0..4 {
reader.read(100);
}
let window_before = reader.current_window();
reader.seek(50000);
reader.read(100);
assert!(reader.current_window() < window_before);
}
#[test]
fn test_adaptive_window_clamped_to_min() {
let data = vec![0u8; 65536];
let config = ReadAheadConfig {
min_window: 4096,
max_window: 65536,
initial_window: 4096, growth_factor_per_mille: 1500,
shrink_factor_per_mille: 100, sequential_threshold: 2,
};
let mut reader = AdaptiveReadAheadReader::with_config(data, config);
reader.read(10);
reader.seek(5000);
reader.read(10);
reader.seek(100);
reader.read(10);
assert!(reader.current_window() >= 4096);
}
#[test]
fn test_adaptive_window_clamped_to_max() {
let data = vec![0u8; 16 * 1024 * 1024];
let config = ReadAheadConfig {
min_window: 1024,
max_window: 65536,
initial_window: 32768,
growth_factor_per_mille: 3000, shrink_factor_per_mille: 500,
sequential_threshold: 1,
};
let mut reader = AdaptiveReadAheadReader::with_config(data, config);
for _ in 0..20 {
reader.read(100);
}
assert!(reader.current_window() <= 65536);
}
#[test]
fn test_adaptive_cache_hit_tracking() {
let data = vec![0u8; 65536];
let mut reader = AdaptiveReadAheadReader::new(data);
reader.read(10);
assert_eq!(reader.stats().cache_misses, 1);
let hits_before = reader.stats().cache_hits;
reader.read(10);
assert!(reader.stats().cache_hits >= hits_before);
}
#[test]
fn test_adaptive_eof_handling() {
let data = vec![1u8, 2, 3];
let mut reader = AdaptiveReadAheadReader::new(data);
let chunk = reader.read(100);
assert_eq!(chunk, vec![1, 2, 3]);
assert!(reader.is_eof());
let empty = reader.read(10);
assert!(empty.is_empty());
}
#[test]
fn test_adaptive_seek() {
let data: Vec<u8> = (0..100).collect();
let mut reader = AdaptiveReadAheadReader::new(data);
assert!(reader.seek(50));
assert_eq!(reader.position(), 50);
let chunk = reader.read(5);
assert_eq!(chunk, vec![50, 51, 52, 53, 54]);
assert!(!reader.seek(200)); assert_eq!(reader.position(), 55); }
#[test]
fn test_adaptive_peek() {
let data: Vec<u8> = (0..100).collect();
let reader = AdaptiveReadAheadReader::new(data);
let peeked = reader.peek(5);
assert_eq!(peeked, &[0, 1, 2, 3, 4]);
assert_eq!(reader.position(), 0); }
#[test]
fn test_adaptive_empty_data() {
let reader = AdaptiveReadAheadReader::new(Vec::new());
assert!(reader.is_empty());
assert!(reader.is_eof());
assert_eq!(reader.remaining(), 0);
}
#[test]
fn test_adaptive_stats_total_bytes() {
let data = vec![0u8; 1000];
let mut reader = AdaptiveReadAheadReader::new(data);
reader.read(100);
reader.read(200);
reader.read(50);
let stats = reader.stats();
assert_eq!(stats.total_reads, 3);
assert_eq!(stats.total_bytes_delivered, 350);
}
}