1#![deny(unsafe_code)]
2
3use std::{
4 cmp::{max, min},
5 fs::File,
6 io::{self, Read, Seek, SeekFrom},
7 ops::Range,
8 path::Path,
9};
10
11use crate::readers::DataRead;
12use memmap2::MmapMut;
13
14pub struct LazyCache<R>
31where
32 R: Read + Seek,
33{
34 source: R,
35 loaded: Vec<bool>,
36 hot_head: Vec<u8>,
37 hot_tail: Vec<u8>,
38 warm: Option<MmapMut>,
39 cold_range: Range<u64>,
40 cold: Vec<u8>,
41 block_size: u64,
42 warm_size: Option<u64>,
43 stream_pos: u64,
44 pos_end: u64,
45}
46
47const BLOCK_SIZE: usize = 4096;
48
49impl<R> DataRead for LazyCache<R>
50where
51 R: Read + Seek,
52{
53 #[inline(always)]
54 fn stream_position(&self) -> u64 {
55 self.stream_pos
56 }
57
58 #[inline]
59 fn read_range(&mut self, range: Range<u64>) -> Result<&[u8], io::Error> {
60 self.get_range_u64(range)
61 }
62
63 fn read_until_any_delim_or_limit(
64 &mut self,
65 delims: &[u8],
66 limit: u64,
67 ) -> Result<&[u8], io::Error> {
68 self._read_while_or_limit(|b| !delims.contains(&b), limit, true)
69 }
70
71 fn read_until_or_limit(&mut self, byte: u8, limit: u64) -> Result<&[u8], io::Error> {
72 self._read_while_or_limit(|b| b != byte, limit, true)
73 }
74
75 fn read_while_or_limit<F>(&mut self, f: F, limit: u64) -> Result<&[u8], io::Error>
76 where
77 F: Fn(u8) -> bool,
78 {
79 self._read_while_or_limit(f, limit, false)
80 }
81
82 fn read_until_utf16_or_limit(
83 &mut self,
84 utf16_char: &[u8; 2],
85 limit: u64,
86 ) -> Result<&[u8], io::Error> {
87 let start = self.stream_pos;
88 let mut end = 0;
89
90 let even_bs = if self.block_size.is_multiple_of(2) {
91 self.block_size
92 } else {
93 self.block_size.saturating_add(1)
94 };
95
96 'outer: while limit.saturating_sub(end) > 0 {
97 let buf = self.read_count(even_bs)?;
98
99 let even = buf
100 .iter()
101 .enumerate()
102 .filter(|(i, _)| i % 2 == 0)
103 .map(|t| t.1);
104
105 let odd = buf
106 .iter()
107 .enumerate()
108 .filter(|(i, _)| i % 2 != 0)
109 .map(|t| t.1);
110
111 for t in even.zip(odd) {
112 if limit.saturating_sub(end) == 0 {
113 break 'outer;
114 }
115
116 end += 2;
117
118 if t.0 == &utf16_char[0] && t.1 == &utf16_char[1] {
120 break 'outer;
122 }
123 }
124
125 if buf.len() as u64 != even_bs {
127 if buf.len() % 2 != 0 {
129 end += 1
131 }
132 break;
133 }
134 }
135
136 self.read_exact_range(start..start + end)
137 }
138
139 #[inline]
140 fn data_size(&self) -> u64 {
141 self.pos_end
142 }
143
144 #[inline]
145 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
146 self.stream_pos = self.offset_from_start(pos);
147 Ok(self.stream_pos)
148 }
149}
150
151impl LazyCache<File> {
152 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
171 Self::from_read_seek(File::open(path)?)
172 }
173}
174
175impl<R> io::Read for LazyCache<R>
176where
177 R: Read + Seek,
178{
179 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
180 let r = self.read_count(buf.len() as u64)?;
181 for (i, b) in r.iter().enumerate() {
182 buf[i] = *b;
183 }
184 Ok(r.len())
185 }
186}
187
188impl<R> LazyCache<R>
189where
190 R: Read + Seek,
191{
192 pub fn from_read_seek(mut rs: R) -> Result<Self, io::Error> {
212 let block_size = BLOCK_SIZE as u64;
213 let pos_end = rs.seek(SeekFrom::End(0))?;
214 let cache_cap = pos_end.div_ceil(BLOCK_SIZE as u64);
215
216 Ok(Self {
217 source: rs,
218 hot_head: vec![],
219 hot_tail: vec![],
220 warm: None,
221 cold_range: 0..0,
222 cold: vec![0; block_size as usize],
223 loaded: vec![false; cache_cap as usize],
224 block_size,
225 warm_size: None,
226 stream_pos: 0,
227 pos_end,
228 })
229 }
230
231 pub fn with_hot_cache(mut self, size: usize) -> Result<Self, io::Error> {
241 let head_tail_size = size / 2;
242
243 let head_size = min(head_tail_size, self.pos_end as usize);
244 self.source.seek(SeekFrom::Start(0))?;
245 self.hot_head.reserve_exact(head_size);
246 self.hot_head.resize(head_size, 0);
247 self.source.read_exact(self.hot_head.as_mut())?;
248
249 if self.pos_end > size as u64 {
250 self.source.seek(SeekFrom::End(-(head_tail_size as i64)))?;
251 self.hot_tail.reserve_exact(head_tail_size);
252 self.hot_tail.resize(head_tail_size, 0);
253 self.source.read_exact(self.hot_tail.as_mut_slice())?;
254 }
255
256 Ok(self)
257 }
258
259 pub fn with_warm_cache(mut self, mut warm_size: u64) -> Self {
267 warm_size = max(warm_size, self.block_size);
270 self.warm_size = Some(warm_size);
271 self
272 }
273
274 #[inline(always)]
275 fn warm(&mut self) -> Result<&mut MmapMut, io::Error> {
276 if self.warm.is_none() && self.warm_size.is_some() {
277 self.warm = Some(MmapMut::map_anon(
278 self.warm_size.unwrap_or_default() as usize
279 )?);
280 }
281 Ok(self
282 .warm
283 .as_mut()
284 .expect("warm cache just initialized above when unset"))
285 }
286
287 #[inline(always)]
288 fn range_warmup(&mut self, range: Range<u64>) -> Result<(), io::Error> {
289 if self.loaded.is_empty() {
290 return Ok(());
291 }
292
293 let start_chunk_id = range.start / self.block_size;
294 let end_chunk_id = (range.end.saturating_sub(1)) / self.block_size;
295
296 self.warm()?;
297 let warm = self
298 .warm
299 .as_mut()
300 .expect("warm() above guarantees the warm cache is initialized");
301
302 let mut chunk_id = start_chunk_id;
303 while chunk_id <= end_chunk_id {
304 if self.loaded[chunk_id as usize] {
305 chunk_id += 1;
306 continue;
307 }
308
309 let run_start = chunk_id;
312 let mut run_end = chunk_id;
313 while run_end < end_chunk_id && !self.loaded[(run_end + 1) as usize] {
314 run_end += 1;
315 }
316
317 let start_offset = run_start * self.block_size;
318 let end_offset = min((run_end + 1) * self.block_size, self.pos_end);
319 let len = (end_offset - start_offset) as usize;
320
321 self.source.seek(SeekFrom::Start(start_offset))?;
322 self.source
323 .read_exact(&mut warm[start_offset as usize..start_offset as usize + len])?;
324
325 self.loaded[run_start as usize..=run_end as usize].fill(true);
326
327 chunk_id = run_end + 1;
328 }
329
330 Ok(())
331 }
332
333 #[inline(always)]
334 fn get_range_u64(&mut self, range: Range<u64>) -> Result<&[u8], io::Error> {
335 let range = if range.end > self.pos_end {
337 range.start..self.pos_end
338 } else {
339 range
340 };
341
342 let range_len = range.end.saturating_sub(range.start);
343
344 if range.start > self.pos_end || range_len == 0 {
345 Ok(&[])
346 } else if range.start < self.hot_head.len() as u64
347 && range.end <= self.hot_head.len() as u64
348 {
349 self.seek(SeekFrom::Start(range.end))?;
350 Ok(&self.hot_head[range.start as usize..range.end as usize])
351 } else if range.start >= (self.pos_end.saturating_sub(self.hot_tail.len() as u64)) {
352 let tail_base = self.pos_end.saturating_sub(self.hot_tail.len() as u64);
353
354 let start = range.start - tail_base;
355 let end = range.end - tail_base;
356
357 self.seek(SeekFrom::Start(range.end))?;
358
359 Ok(&self.hot_tail[start as usize..end as usize])
360 } else if range.end < self.warm_size.unwrap_or_default() {
361 self.range_warmup(range.clone())?;
362 self.seek(SeekFrom::Start(range.end))?;
363
364 Ok(&self.warm()?[range.start as usize..range.end as usize])
365 } else {
366 if self.cold_range.contains(&range.start)
367 && self.cold_range.contains(&range.end.saturating_sub(1))
368 {
369 let rel_start = range.start - self.cold_range.start;
370 self.seek(SeekFrom::Start(range.end))?;
371
372 Ok(&self.cold[rel_start as usize..(rel_start + range_len) as usize])
373 } else {
374 let range_len_ext = range_len.saturating_add(self.block_size);
376 if range_len_ext > self.cold.len() as u64 {
377 self.cold.resize(range_len_ext as usize, 0);
378 }
379
380 self.source.seek(SeekFrom::Start(range.start))?;
381 let n = self
382 .source
383 .read(self.cold[..range_len_ext as usize].as_mut())?;
384 self.cold_range = range.start..range.start + n as u64;
385 self.seek(SeekFrom::Start(range.end))?;
386
387 Ok(&self.cold[..min(range_len as usize, n)])
388 }
389 }
390 }
391
392 #[inline(always)]
394 fn _read_while_or_limit<F>(
395 &mut self,
396 f: F,
397 limit: u64,
398 include_last: bool,
399 ) -> Result<&[u8], io::Error>
400 where
401 F: Fn(u8) -> bool,
402 {
403 let start = self.stream_pos;
404 let mut end = 0;
405
406 'outer: while limit - end > 0 {
407 let buf = self.read_count(self.block_size)?;
408
409 for b in buf {
410 if limit - end == 0 {
411 break 'outer;
412 }
413
414 if !f(*b) {
415 if include_last && end < self.data_size() {
416 end += 1;
417 }
418 break 'outer;
420 }
421
422 end += 1;
423 }
424
425 if buf.len() as u64 != self.block_size {
427 break;
428 }
429 }
430
431 self.read_exact_range(start..start + end)
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use std::os::unix::fs::MetadataExt;
438
439 use super::*;
440
441 macro_rules! lazy_cache {
442 ($content: literal) => {
443 LazyCache::from_read_seek(std::io::Cursor::new($content)).unwrap()
444 };
445 }
446
447 macro_rules! read_to_end {
449 ($r: expr, $cs: literal) => {{
450 let mut buf = [0u8; $cs];
451 let mut out: Vec<u8> = vec![];
452 while let Ok(n) = $r.read(&mut buf[..]) {
453 if n == 0 {
454 break;
455 }
456 out.extend(&buf[..n]);
457 }
458 out
459 }};
460 }
461
462 #[test]
463 fn test_get_single_block() {
464 let mut cache = lazy_cache!(b"hello world");
465 let data = cache.read_range(0..4).unwrap();
466 assert_eq!(data, b"hell");
467 }
468
469 #[test]
470 fn test_get_across_blocks() {
471 let mut cache = lazy_cache!(b"hello world");
472 let data = cache.read_range(2..7).unwrap();
473 assert_eq!(data, b"llo w");
474 }
475
476 #[test]
477 fn test_get_entire_file() {
478 let mut cache = lazy_cache!(b"hello world");
479 let data = cache.read_range(0..11).unwrap();
480 assert_eq!(data, b"hello world");
481 }
482
483 #[test]
484 fn test_get_empty_range() {
485 let mut cache = lazy_cache!(b"hello world");
486 let data = cache.read_range(0..0).unwrap();
487 assert!(data.is_empty());
488 }
489
490 #[test]
491 fn test_get_out_of_bounds() {
492 let mut cache = lazy_cache!(b"hello world");
493 assert!(cache.read_range(20..30).unwrap().is_empty());
497 }
498
499 #[test]
500 fn test_cache_eviction() {
501 let mut cache = lazy_cache!(b"0123456789abcdef");
502 let _ = cache.read_range(0..8).unwrap();
504 let _ = cache.read_range(8..12).unwrap();
506 let data = cache.read_range(8..12).unwrap();
508 assert_eq!(data, b"89ab");
509 }
510
511 #[test]
512 fn test_chunk_consolidation() {
513 let mut cache = lazy_cache!(b"0123456789abcdef");
514 let _ = cache.read_range(0..4).unwrap();
516 let _ = cache.read_range(4..8).unwrap();
517 let _ = cache.read_range(8..12).unwrap();
519 let _ = cache.read_range(2..6).unwrap();
521 let data = cache.read_range(0..8).unwrap();
523 assert_eq!(data, b"01234567");
524 }
525
526 #[test]
527 fn test_overlapping_ranges() {
528 let mut cache = lazy_cache!(b"0123456789abcdef");
529 let _ = cache.read_range(2..6).unwrap();
531 let _ = cache.read_range(4..10).unwrap();
532 let data = cache.read_range(2..10).unwrap();
534 assert_eq!(data, b"23456789");
535 }
536
537 #[test]
538 fn test_lru_behavior() {
539 let mut cache = lazy_cache!(b"0123456789abcdef");
540 let _ = cache.read_range(0..4).unwrap();
542 let _ = cache.read_range(4..8).unwrap();
544 let _ = cache.read_range(8..12).unwrap();
546 let data = cache.read_range(0..4).unwrap();
548 assert_eq!(data, b"0123");
549 }
550
551 #[test]
552 fn test_small_block_size() {
553 let mut cache = lazy_cache!(b"abc");
554 let data = cache.read_range(0..3).unwrap();
555 assert_eq!(data, b"abc");
556 }
557
558 #[test]
559 fn test_large_block_size() {
560 let mut cache = lazy_cache!(b"hello world");
561 let data = cache.read_range(0..11).unwrap();
562 assert_eq!(data, b"hello world");
563 }
564
565 #[test]
566 fn test_file_smaller_than_block() {
567 let mut cache = lazy_cache!(b"abc");
568 let data = cache.read_range(0..3).unwrap();
569 assert_eq!(data, b"abc");
570 }
571
572 #[test]
573 fn test_multiple_gets_same_block() {
574 let mut cache = lazy_cache!(b"0123456789abcdef");
575 let _ = cache.read_range(0..4).unwrap();
577 let _ = cache.read_range(0..4).unwrap();
578 let _ = cache.read_range(0..4).unwrap();
579 let data = cache.read_range(0..4).unwrap();
581 assert_eq!(data, b"0123");
582 }
583
584 #[test]
585 fn test_read_method() {
586 let mut cache = lazy_cache!(b"hello world");
587 let _ = cache.read_count(6).unwrap();
588 let data = cache.read_count(5).unwrap();
589 assert_eq!(data, b"world");
590 assert!(cache.read_count(1).unwrap().is_empty());
592 }
593
594 #[test]
595 fn test_read_empty() {
596 let mut cache = lazy_cache!(b"hello world");
597 let data = cache.read_count(0).unwrap();
598 assert!(data.is_empty());
599 }
600
601 #[test]
602 fn test_read_beyond_end() {
603 let mut cache = lazy_cache!(b"hello world");
604 let _ = cache.read_count(11).unwrap();
605 let data = cache.read_count(5).unwrap();
606 assert!(data.is_empty());
607 }
608
609 #[test]
610 fn test_read_exact_range() {
611 let mut cache = lazy_cache!(b"hello world");
612 let data = cache.read_exact_range(0..5).unwrap();
613 assert_eq!(data, b"hello");
614 assert_eq!(cache.read_exact_range(5..11).unwrap(), b" world");
615 assert!(cache.read_exact_range(12..13).is_err());
616 }
617
618 #[test]
619 fn test_read_exact_range_error() {
620 let mut cache = lazy_cache!(b"hello world");
621 let result = cache.read_exact_range(0..20);
622 assert!(result.is_err());
623 }
624
625 #[test]
626 fn test_read_exact() {
627 let mut cache = lazy_cache!(b"hello world");
628 let data = cache.read_exact_count(5).unwrap();
629 assert_eq!(data, b"hello");
630 assert_eq!(cache.read_exact_count(6).unwrap(), b" world");
631 assert!(cache.read_exact_count(0).is_ok());
632 assert!(cache.read_exact_count(1).is_err());
633 }
634
635 #[test]
636 fn test_read_exact_error() {
637 let mut cache = lazy_cache!(b"hello world");
638 let result = cache.read_exact_count(20);
639 assert!(result.is_err());
640 }
641
642 #[test]
643 fn test_read_until_limit() {
644 let mut cache = lazy_cache!(b"hello world");
645 let data = cache.read_until_or_limit(b' ', 10).unwrap();
646 assert_eq!(data, b"hello ");
647 assert_eq!(cache.read_exact_count(5).unwrap(), b"world");
648 }
649
650 #[test]
651 fn test_read_until_limit_not_found() {
652 let mut cache = lazy_cache!(b"hello world");
653 let data = cache.read_until_or_limit(b'\n', 11).unwrap();
654 assert_eq!(data, b"hello world");
655 assert!(cache.read_count(1).unwrap().is_empty());
656 }
657
658 #[test]
659 fn test_read_until_limit_beyond_stream() {
660 let mut cache = lazy_cache!(b"hello world");
661 let data = cache.read_until_or_limit(b'\n', 42).unwrap();
662 assert_eq!(data, b"hello world");
663 assert!(cache.read_count(1).unwrap().is_empty());
664 }
665
666 #[test]
667 fn test_read_until_limit_with_limit() {
668 let mut cache = lazy_cache!(b"hello world");
669 let data = cache.read_until_or_limit(b' ', 42).unwrap();
670 assert_eq!(data, b"hello ");
671
672 let data = cache.read_until_or_limit(b' ', 2).unwrap();
673 assert_eq!(data, b"wo");
674
675 let data = cache.read_until_or_limit(b' ', 42).unwrap();
676 assert_eq!(data, b"rld");
677 }
678
679 #[test]
680 fn test_read_until_utf16_limit() {
681 let mut cache = lazy_cache!(
682 b"\x61\x00\x62\x00\x63\x00\x64\x00\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x00"
683 );
684 let data = cache.read_until_utf16_or_limit(b"\x00\x00", 512).unwrap();
685 assert_eq!(data, b"\x61\x00\x62\x00\x63\x00\x64\x00\x00\x00");
686
687 let data = cache.read_until_utf16_or_limit(b"\x00\x00", 1).unwrap();
688 assert_eq!(data, b"\x61\x00");
689
690 assert_eq!(
691 cache.read_until_utf16_or_limit(b"\xff\xff", 64).unwrap(),
692 b"\x62\x00\x63\x00\x64\x00\x00"
693 );
694 }
695
696 #[test]
697 fn test_io_read() {
698 let p = "./src/lib.rs";
699 let mut f = File::open(p).unwrap();
700 let mut lr = LazyCache::from_read_seek(File::open(p).unwrap())
701 .unwrap()
702 .with_hot_cache(512)
703 .unwrap()
704 .with_warm_cache(1024);
705
706 let fb = read_to_end!(f, 32);
707 let lcb = read_to_end!(lr, 16);
708
709 assert_eq!(lcb, fb);
710 }
711
712 #[test]
713 fn test_data_size() {
714 let f = File::open("./src/lib.rs").unwrap();
715 let size = f.metadata().unwrap().size();
716
717 let c = LazyCache::from_read_seek(f).unwrap();
718 assert_eq!(size, c.data_size());
719
720 assert_eq!(
721 LazyCache::from_read_seek(io::Cursor::new(&[]))
722 .unwrap()
723 .data_size(),
724 0
725 );
726 }
727}