use super::{parse_control, Stanza};
use std::num::NonZeroUsize;
use std::sync::{Arc, Mutex};
use tracing::*;
struct Index_ {
map: memmap::Mmap,
lru: Mutex<lru::LruCache<String, Option<(usize, usize)>>>,
}
#[derive(Clone)]
pub struct Index(Arc<Index_>);
impl Index {
pub fn open<P: AsRef<std::path::Path>>(f: P) -> Result<Self, std::io::Error> {
let file = std::fs::File::open(f)?;
let mmap = unsafe { memmap::MmapOptions::new().map(&file)? };
Ok(Index(Arc::new(Index_ {
map: mmap,
lru: Mutex::new(lru::LruCache::new(NonZeroUsize::new(256).unwrap())),
})))
}
pub async fn lookup_async<'a>(&'a self, package: &str) -> Option<Stanza<'a>> {
let s = self.clone();
let p = package.to_string();
tokio::task::spawn_blocking(move || s.lookup_range(&p))
.await
.unwrap()
.map(|(m1, m2)| {
let s = &*self.0.map;
let line = std::str::from_utf8(&s[m1..m2]).unwrap().trim();
parse_control(line).unwrap().1
})
}
pub async fn lookup_virtual_async<'a>(&'a self, package: &str) -> Vec<Stanza<'a>> {
let s = self.clone();
let r =
regex::bytes::Regex::new(&format!(r#"\nProvides:[^\n]*[, ]{package}[,\s\n]"#)).unwrap();
tokio::task::spawn_blocking(move || {
r.find_iter(&*s.0.map)
.map(|m| {
let map = &*s.0.map;
s.frame(0, map.len(), m.start())
})
.collect::<Vec<_>>()
})
.await
.unwrap()
.into_iter()
.map(|(m1, m2)| {
let line = std::str::from_utf8(&self.0.map[m1..m2]).unwrap().trim();
parse_control(line).unwrap().1
})
.collect()
}
pub fn lookup<'a>(&'a self, package: &str) -> Option<Stanza<'a>> {
self.lookup_range(package).map(|(m1, m2)| {
let s = &*self.0.map;
let line = std::str::from_utf8(&s[m1..m2]).unwrap().trim();
parse_control(line).unwrap().1
})
}
fn frame(&self, a: usize, b: usize, mut m1: usize) -> (usize, usize) {
let s = &*self.0.map;
while m1 > a && (s[m1] != b'\n' || s[m1 + 1] != b'\n') {
m1 -= 1;
}
let mut m2 = m1 + 2;
while m2 + 1 < b && (s[m2] != b'\n' || s[m2 + 1] != b'\n') {
m2 += 1;
}
if m2 < b {
m2 += 1
}
(m1, m2)
}
fn lookup_range(&self, package: &str) -> Option<(usize, usize)> {
info!("Looking up {:?}", package);
let s = &*self.0.map;
match self.0.lru.lock().unwrap().get(package) {
Some(Some((m1, m2))) => {
return Some((*m1, *m2));
}
Some(None) => return None,
None => {}
}
let mut a = 0;
let mut b = s.len();
loop {
let (m1, m2) = self.frame(a, b, (a + b) / 2);
let line = std::str::from_utf8(&s[m1..m2]).unwrap().trim();
if line.is_empty() {
self.0.lru.lock().unwrap().put(package.to_string(), None);
return None;
}
if let Ok((_, st)) = parse_control(line) {
if st.package == package {
self.0
.lru
.lock()
.unwrap()
.put(package.to_string(), Some((m1, m2)));
return Some((m1, m2));
} else if st.package < package {
a = m2
} else {
b = m1
}
} else {
panic!("parse {:?}", line);
}
if a >= b {
break;
}
}
debug!("not found 2: {:?}", package);
self.0.lru.lock().unwrap().put(package.to_string(), None);
None
}
}