engramdb_core/
count_index.rs1use std::collections::HashMap;
7use std::io::{BufWriter, Read, Write};
8use std::path::Path;
9
10#[derive(Debug, Default)]
11pub struct CountIndex {
12 counts: HashMap<u64, u32>,
13}
14
15impl CountIndex {
16 pub fn build_from_bin_stream(mut src: impl Read) -> std::io::Result<Self> {
18 let mut counts = HashMap::<u64, u32>::new();
19 let mut buf = [0u8; 8];
20 loop {
21 let n = src.read(&mut buf)?;
22 if n == 0 {
23 break;
24 }
25 if n != 8 {
26 return Err(std::io::Error::new(
27 std::io::ErrorKind::UnexpectedEof,
28 "partial rowid",
29 ));
30 }
31 let rowid = u64::from_le_bytes(buf);
32 *counts.entry(rowid).or_insert(0) += 1;
33 }
34 Ok(Self { counts })
35 }
36
37 pub fn count(&self, rowid: u64) -> u64 {
38 self.counts.get(&rowid).copied().unwrap_or(0) as u64
39 }
40
41 pub fn iter(&self) -> impl Iterator<Item = (u64, u32)> {
42 let mut v: Vec<_> = self.counts.iter().map(|(&a, &b)| (a, b)).collect();
43 v.sort_unstable_by_key(|&(a, _)| a);
44 v.into_iter()
45 }
46
47 pub fn write_bin(&self, path: &Path) -> std::io::Result<()> {
49 let mut w = BufWriter::new(std::fs::File::create(path)?);
50 for (rowid, count) in self.iter() {
51 w.write_all(&rowid.to_le_bytes())?;
52 w.write_all(&count.to_le_bytes())?;
53 }
54 w.flush()
55 }
56
57 pub fn write_dump(&self, path: &Path) -> std::io::Result<()> {
58 let mut w = BufWriter::new(std::fs::File::create(path)?);
59 for (rowid, count) in self.iter() {
60 writeln!(w, "{} {}", rowid, count)?;
61 }
62 w.flush()
63 }
64
65 pub fn from_bin(path: &Path) -> std::io::Result<Self> {
66 let data = std::fs::read(path)?;
67 let mut counts = HashMap::new();
68 for chunk in data.chunks(12) {
69 if chunk.len() != 12 {
70 break;
71 }
72 let rowid = u64::from_le_bytes(chunk[..8].try_into().unwrap());
73 let c = u32::from_le_bytes(chunk[8..12].try_into().unwrap());
74 counts.insert(rowid, c);
75 }
76 Ok(Self { counts })
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn build_count_write_read() {
86 let data: Vec<u8> = [5u64, 3, 5, 7, 5]
87 .iter()
88 .flat_map(|x| x.to_le_bytes())
89 .collect();
90 let idx = CountIndex::build_from_bin_stream(&data[..]).unwrap();
91 assert_eq!(idx.count(5), 3);
92 assert_eq!(idx.count(3), 1);
93 let p = std::env::temp_dir().join("ct_test.bin");
94 idx.write_bin(&p).unwrap();
95 let idx2 = CountIndex::from_bin(&p).unwrap();
96 assert_eq!(idx2.count(7), 1);
97 }
98}