Skip to main content

qualia_cli/ingest/
writer.rs

1use qualia_core_db::NQuin;
2use std::fs::File;
3use std::io::{BufWriter, Write}; // Using NQuin
4
5pub struct SuperBlockWriter {
6    writer: BufWriter<File>,
7    buffer: [NQuin; 850],
8    cursor: usize,
9    blocks_written: u32,
10}
11
12impl SuperBlockWriter {
13    pub fn new(output_path: &std::path::Path) -> std::io::Result<Self> {
14        let file = File::create(output_path)?;
15        // Use a large system buffer to prevent constant OS context switches
16        let writer = BufWriter::with_capacity(1024 * 1024, file);
17
18        Ok(Self {
19            writer,
20            buffer: [NQuin::default(); 850],
21            cursor: 0,
22            blocks_written: 0,
23        })
24    }
25
26    #[inline(always)]
27    pub fn push(&mut self, quin: NQuin) -> std::io::Result<()> {
28        self.buffer[self.cursor] = quin;
29        self.cursor += 1;
30
31        if self.cursor == 850 {
32            self.flush_block()?;
33        }
34        Ok(())
35    }
36
37    pub fn flush_block(&mut self) -> std::io::Result<()> {
38        if self.cursor == 0 {
39            return Ok(());
40        }
41
42        // 1. Build the 160-byte Header
43        // [4 bytes Magic Number "Q42B"] + [4 bytes block index] + [152 bytes reserved/padding]
44        let mut header = [0u8; 160];
45        header[0..4].copy_from_slice(b"Q42B");
46        header[4..8].copy_from_slice(&self.blocks_written.to_le_bytes());
47
48        // 2. Write Header
49        self.writer.write_all(&header)?;
50
51        // 3. Write Data (Zero-cost slice cast via bytemuck)
52        // Note: If cursor < 850 (e.g. final block), we still write the full 850
53        // to maintain strict 40KB alignment, leaving trailing structs as Zero/Default.
54        let bytes: &[u8] = bytemuck::cast_slice(&self.buffer);
55        self.writer.write_all(bytes)?;
56
57        // 4. Reset Buffer
58        self.cursor = 0;
59        // Zero out buffer so leftover old records aren't written if this is the last block
60        self.buffer = [NQuin::default(); 850];
61        self.blocks_written += 1;
62
63        Ok(())
64    }
65}
66
67impl Drop for SuperBlockWriter {
68    fn drop(&mut self) {
69        // Ensure final partial block is flushed when the writer goes out of scope
70        let _ = self.flush_block();
71        let _ = self.writer.flush();
72    }
73}