Skip to main content

qualia_core_db/q42/volume/
opfs_source.rs

1//! WASM/OPFS caller-buffered Q42 range source.
2//!
3//! JS or native OPFS fills a caller-owned buffer; Rust never maps the whole
4//! file on the hot path. A byte slice is accepted when the host already holds
5//! the bytes (tests, verified CAR entity after cold construction). The
6//! callback adapter is the browser path: OPFS/`File` reads into `out` and
7//! returns the filled count.
8//!
9//! Local CARv1 verification is a separate cold construction. It calls
10//! [`decode_and_verify_car`] and then exposes concatenated raw (0x55) UnixFS
11//! entity bytes through the same [`Q42RangeSource`] contract. Q42 offsets
12//! address that entity, never the CAR envelope. This module does not contact
13//! a public IPFS gateway.
14
15use std::io;
16use std::path::Path;
17
18use super::car::{decode_and_verify_car, VerifiedCarBlock};
19use super::cid::CidSha256;
20use super::range::{validate_exact_range_response, Q42ByteRange, Q42RangeSource};
21
22fn invalid(message: impl Into<String>) -> io::Error {
23    io::Error::new(io::ErrorKind::InvalidInput, message.into())
24}
25
26fn short_fill() -> io::Error {
27    io::Error::new(
28        io::ErrorKind::InvalidData,
29        "OPFS range fill did not return exactly the requested Q42 bytes",
30    )
31}
32
33/// Caller-buffered view of host-supplied bytes (slice or owned entity).
34pub struct OpfsSliceRangeSource<B> {
35    bytes: B,
36}
37
38impl<B> OpfsSliceRangeSource<B> {
39    pub fn new(bytes: B) -> Self {
40        Self { bytes }
41    }
42}
43
44impl<B: AsRef<[u8]>> OpfsSliceRangeSource<B> {
45    pub fn as_bytes(&self) -> &[u8] {
46        self.bytes.as_ref()
47    }
48}
49
50impl<B: AsRef<[u8]>> Q42RangeSource for OpfsSliceRangeSource<B> {
51    fn length(&self) -> io::Result<u64> {
52        Ok(self.bytes.as_ref().len() as u64)
53    }
54
55    fn read_range_into(&self, range: Q42ByteRange, out: &mut [u8]) -> io::Result<()> {
56        copy_exact_slice(self.bytes.as_ref(), range, out)
57    }
58}
59
60/// Host fill callback: `(offset, length, buf) -> bytes_written`.
61///
62/// The callback must write exactly `length` bytes into `buf`. A short fill is
63/// never treated as success. Rust does not open or map the backing file.
64pub struct OpfsCallbackRangeSource<F> {
65    length: u64,
66    fill: F,
67}
68
69impl<F> OpfsCallbackRangeSource<F>
70where
71    F: Fn(u64, usize, &mut [u8]) -> io::Result<usize>,
72{
73    pub fn new(length: u64, fill: F) -> Self {
74        Self { length, fill }
75    }
76}
77
78impl<F> Q42RangeSource for OpfsCallbackRangeSource<F>
79where
80    F: Fn(u64, usize, &mut [u8]) -> io::Result<usize>,
81{
82    fn length(&self) -> io::Result<u64> {
83        Ok(self.length)
84    }
85
86    fn read_range_into(&self, range: Q42ByteRange, out: &mut [u8]) -> io::Result<()> {
87        if out.len() != range.length {
88            return Err(invalid(
89                "Q42 range output buffer length does not match request",
90            ));
91        }
92        range.validate_for(self.length)?;
93        let filled = (self.fill)(range.offset, range.length, out)?;
94        if filled != range.length {
95            return Err(short_fill());
96        }
97        validate_exact_range_response(range, self.length, range.offset, filled)
98    }
99}
100
101/// Verified UnixFS entity reconstructed from a local CARv1 file.
102pub type VerifiedCarRangeSource = OpfsSliceRangeSource<Vec<u8>>;
103
104/// Decode and CID-verify a CARv1 buffer, then expose raw-leaf entity bytes.
105pub fn verify_car_bytes_as_q42_source(car: &[u8]) -> io::Result<VerifiedCarRangeSource> {
106    let blocks = decode_and_verify_car(car)?;
107    Ok(OpfsSliceRangeSource::new(concatenate_raw_leaves(&blocks)))
108}
109
110/// Read a local CARv1 path, CID-verify every block, expose entity bytes.
111///
112/// Cold construction: the CAR is loaded to run [`decode_and_verify_car`]. The
113/// returned source then serves exact ranges of the UnixFS entity. This is not
114/// a live public-gateway check.
115pub fn verify_local_car_as_q42_source(path: &Path) -> io::Result<VerifiedCarRangeSource> {
116    let car = std::fs::read(path)?;
117    verify_car_bytes_as_q42_source(&car)
118}
119
120fn concatenate_raw_leaves(blocks: &[VerifiedCarBlock]) -> Vec<u8> {
121    let mut total = 0usize;
122    for block in blocks {
123        if block.cid.codec == CidSha256::RAW {
124            total = total.saturating_add(block.data.len());
125        }
126    }
127    let mut entity = Vec::with_capacity(total);
128    for block in blocks {
129        if block.cid.codec == CidSha256::RAW {
130            entity.extend_from_slice(&block.data);
131        }
132    }
133    entity
134}
135
136fn copy_exact_slice(bytes: &[u8], range: Q42ByteRange, out: &mut [u8]) -> io::Result<()> {
137    if out.len() != range.length {
138        return Err(invalid(
139            "Q42 range output buffer length does not match request",
140        ));
141    }
142    let source_length = bytes.len() as u64;
143    range.validate_for(source_length)?;
144    let start = usize::try_from(range.offset)
145        .map_err(|_| invalid("Q42 range offset exceeds platform"))?;
146    let end = start
147        .checked_add(range.length)
148        .ok_or_else(|| invalid("Q42 range overflows platform usize"))?;
149    out.copy_from_slice(&bytes[start..end]);
150    Ok(())
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156    use crate::q42_volume::write_unified_volume;
157    use crate::specialized_libs::computational_geometry::allocation_counter::assert_zero_alloc;
158    use crate::NQuin;
159    use std::collections::HashMap;
160    use std::io::Write;
161
162    use super::super::car::encode_raw_car;
163    use super::super::range::LocalFileRangeSource;
164    use tempfile::NamedTempFile;
165
166    fn tiny_unified_volume() -> NamedTempFile {
167        let file = NamedTempFile::new().unwrap();
168        write_unified_volume(
169            file.path(),
170            &HashMap::new(),
171            &[(3, 3)],
172            &[vec![NQuin {
173                subject: 1,
174                predicate: 2,
175                object: 3,
176                context: 0,
177                metadata: 0,
178                parity: 0,
179            }]],
180        )
181        .unwrap();
182        file
183    }
184
185    fn read_all<S: Q42RangeSource>(source: &S) -> Vec<u8> {
186        let length = usize::try_from(source.length().unwrap()).unwrap();
187        let mut out = vec![0u8; length];
188        source
189            .read_range_into(
190                Q42ByteRange {
191                    offset: 0,
192                    length,
193                },
194                &mut out,
195            )
196            .unwrap();
197        out
198    }
199
200    fn assert_same_ranges<A: Q42RangeSource, B: Q42RangeSource>(left: &A, right: &B) {
201        let left_len = left.length().unwrap();
202        let right_len = right.length().unwrap();
203        assert_eq!(left_len, right_len);
204        assert_eq!(read_all(left), read_all(right));
205
206        let mid_len = 16.min(left_len as usize);
207        if left_len >= 8 && mid_len > 0 {
208            let mut from_left = vec![0u8; mid_len];
209            let mut from_right = vec![0u8; mid_len];
210            let range = Q42ByteRange {
211                offset: 4,
212                length: mid_len,
213            };
214            left.read_range_into(range, &mut from_left).unwrap();
215            right.read_range_into(range, &mut from_right).unwrap();
216            assert_eq!(from_left, from_right);
217        }
218
219        let overflow = Q42ByteRange {
220            offset: left_len,
221            length: 1,
222        };
223        assert!(left.read_range_into(overflow, &mut [0u8; 1]).is_err());
224        assert!(right.read_range_into(overflow, &mut [0u8; 1]).is_err());
225    }
226
227    #[test]
228    fn slice_source_matches_local_file_on_tiny_unified_volume() {
229        let file = tiny_unified_volume();
230        let disk = LocalFileRangeSource::open(file.path()).unwrap();
231        let bytes = std::fs::read(file.path()).unwrap();
232        let slice = OpfsSliceRangeSource::new(bytes.as_slice());
233        assert_same_ranges(&disk, &slice);
234        assert_eq!(slice.as_bytes(), bytes.as_slice());
235    }
236
237    #[test]
238    fn callback_source_matches_local_file_on_tiny_unified_volume() {
239        let file = tiny_unified_volume();
240        let disk = LocalFileRangeSource::open(file.path()).unwrap();
241        let bytes = std::fs::read(file.path()).unwrap();
242        let callback = OpfsCallbackRangeSource::new(bytes.len() as u64, |offset, len, buf| {
243            copy_exact_slice(
244                &bytes,
245                Q42ByteRange {
246                    offset,
247                    length: len,
248                },
249                buf,
250            )?;
251            Ok(len)
252        });
253        assert_same_ranges(&disk, &callback);
254    }
255
256    #[test]
257    fn verified_local_car_exposes_the_same_entity_as_the_q42_file() {
258        let file = tiny_unified_volume();
259        let entity = std::fs::read(file.path()).unwrap();
260        let car = encode_raw_car(&[entity.as_slice()]);
261        let mut car_file = NamedTempFile::new().unwrap();
262        car_file.write_all(&car).unwrap();
263        car_file.flush().unwrap();
264
265        let source = verify_local_car_as_q42_source(car_file.path()).unwrap();
266        assert_eq!(source.as_bytes(), entity.as_slice());
267
268        let disk = LocalFileRangeSource::open(file.path()).unwrap();
269        assert_same_ranges(&disk, &source);
270    }
271
272    #[test]
273    fn tampered_local_car_is_rejected() {
274        let mut car = encode_raw_car(&[b"intact-q42-entity".as_slice()]);
275        *car.last_mut().unwrap() ^= 0xff;
276        let mut car_file = NamedTempFile::new().unwrap();
277        car_file.write_all(&car).unwrap();
278        car_file.flush().unwrap();
279        assert!(verify_local_car_as_q42_source(car_file.path()).is_err());
280        assert!(verify_car_bytes_as_q42_source(&car).is_err());
281    }
282
283    #[test]
284    fn short_callback_fill_is_rejected() {
285        let source = OpfsCallbackRangeSource::new(8, |_offset, _len, buf| {
286            buf[..2].copy_from_slice(b"ab");
287            Ok(2)
288        });
289        assert!(source
290            .read_range_into(
291                Q42ByteRange {
292                    offset: 0,
293                    length: 4
294                },
295                &mut [0u8; 4]
296            )
297            .is_err());
298    }
299
300    #[test]
301    fn slice_and_callback_hot_reads_are_zero_heap() {
302        let file = tiny_unified_volume();
303        let bytes = std::fs::read(file.path()).unwrap();
304        let slice = OpfsSliceRangeSource::new(bytes.as_slice());
305        let callback = OpfsCallbackRangeSource::new(bytes.len() as u64, |offset, len, buf| {
306            copy_exact_slice(
307                &bytes,
308                Q42ByteRange {
309                    offset,
310                    length: len,
311                },
312                buf,
313            )?;
314            Ok(len)
315        });
316        let mut slice_buf = [0u8; 8];
317        let mut callback_buf = [0u8; 8];
318        let range = Q42ByteRange {
319            offset: 0,
320            length: 8,
321        };
322        assert_zero_alloc("q42_opfs_slice_range_read", || {
323            slice.read_range_into(range, &mut slice_buf).unwrap();
324        });
325        assert_zero_alloc("q42_opfs_callback_range_read", || {
326            callback
327                .read_range_into(range, &mut callback_buf)
328                .unwrap();
329        });
330        assert_eq!(slice_buf, callback_buf);
331    }
332}