greplm_core/io_backend.rs
1//! Ingest read path.
2//!
3//! Reading thousands of small source files is dominated by per-file syscall
4//! overhead, not bandwidth, so the goal is high effective queue depth (many
5//! reads in flight) rather than exotic zero-copy. The portable backend achieves
6//! that with a rayon worker pool over buffered reads; the page cache keeps
7//! re-reads (and the watch loop) cheap. An io_uring backend can be slotted in on
8//! Linux behind the `io-uring` feature.
9
10use std::path::Path;
11
12use crate::error::{Error, Result};
13
14/// Abstraction over how file bytes are pulled in during indexing.
15pub trait IoBackend: Send + Sync {
16 /// Read the full contents of a file.
17 fn read(&self, path: &Path) -> Result<Vec<u8>>;
18
19 /// Human-readable backend name (for `status`).
20 fn name(&self) -> &'static str;
21}
22
23/// Portable backend: a plain buffered read. Concurrency is supplied by the
24/// caller running `read` from many rayon worker threads at once.
25#[derive(Debug, Default, Clone, Copy)]
26pub struct RayonBackend;
27
28impl IoBackend for RayonBackend {
29 fn read(&self, path: &Path) -> Result<Vec<u8>> {
30 std::fs::read(path).map_err(|e| Error::io(path, e))
31 }
32
33 fn name(&self) -> &'static str {
34 "rayon"
35 }
36}
37
38#[cfg(all(feature = "io-uring", target_os = "linux"))]
39mod uring {
40 use super::*;
41
42 /// Linux io_uring backend (registered buffers + SQPOLL planned). Currently a
43 /// thin placeholder that defers to a buffered read so the feature compiles
44 /// and can be filled in without changing call sites.
45 #[derive(Debug, Default, Clone, Copy)]
46 pub struct UringBackend;
47
48 impl IoBackend for UringBackend {
49 fn read(&self, path: &Path) -> Result<Vec<u8>> {
50 std::fs::read(path).map_err(|e| Error::io(path, e))
51 }
52
53 fn name(&self) -> &'static str {
54 "io_uring"
55 }
56 }
57}
58
59/// Select a backend based on configuration and build features.
60pub fn default_backend() -> Box<dyn IoBackend> {
61 Box::new(RayonBackend)
62}