OXYGENHUB

Coverage / crates/oxy-core/src/flat.rs 98.0%

100 of 102 lines run, 51 of 54 functions · run #401 on subrepo-pin-state · run missed

1
//! A flat binary layout for local indexes (oxygen#41): fixed-width
2
//! little-endian numbers and length-prefixed bytes, read in one pass
3
//! without building a generic value tree. Only for caches this repository
4
//! derives and can derive again: never exchanged, never trusted beyond the
5
//! key they are stored under, and a reader that finds anything amiss fails
6
//! so the caller rebuilds.
7
8
use crate::hash::ObjectRef;
9
use crate::objects::ObjectError;
10
11
#[derive(Default)]
12
pub struct Writer(pub Vec<u8>);
13
14
impl Writer {
15356374×
    pub fn u8(&mut self, v: u8) {
16356374×
        self.0.push(v);
17356374×
    }
18396196×
    pub fn u32(&mut self, v: u32) {
19396196×
        self.0.extend_from_slice(&v.to_le_bytes());
20396196×
    }
2172252×
    pub fn u64(&mut self, v: u64) {
2272252×
        self.0.extend_from_slice(&v.to_le_bytes());
2372252×
    }
24
    /// A count, which must fit in 32 bits.
25190162×
    pub fn count(&mut self, n: usize) {
26190162×
        self.u32(u32::try_from(n).expect("an index count fits in 32 bits"));
27190162×
    }
28715175×
    pub fn fixed(&mut self, b: &[u8]) {
29715175×
        self.0.extend_from_slice(b);
30715175×
    }
31115989×
    pub fn bytes(&mut self, b: &[u8]) {
32115989×
        self.count(b.len());
33115989×
        self.0.extend_from_slice(b);
34115989×
    }
35251555×
    pub fn object(&mut self, r: &ObjectRef) {
36251555×
        self.fixed(&r.0);
37251555×
    }
3831662×
    pub fn opt_object(&mut self, r: &Option<ObjectRef>) {
3931662×
        match r {
4031345×
            None => self.u8(0),
41317×
            Some(r) => {
42317×
                self.u8(1);
43317×
                self.object(r);
44317×
            }
45
        }
4631662×
    }
47
}
48
49
pub struct Reader<'a> {
50
    buf: &'a [u8],
51
    pos: usize,
52
}
53
543×
fn short() -> ObjectError {
553×
    ObjectError::Schema("index: truncated".into())
563×
}
57
58
impl<'a> Reader<'a> {
5925988×
    pub fn new(buf: &'a [u8]) -> Reader<'a> {
6025988×
        Reader { buf, pos: 0 }
6125988×
    }
6247552×
    pub fn at_end(&self) -> bool {
6347552×
        self.pos == self.buf.len()
6447552×
    }
651241538×
    fn take(&mut self, n: usize) -> Result<&'a [u8], ObjectError> {
661241538×
        let end = self.pos.checked_add(n).ok_or_else(short)?;
671241538×
        let s = self.buf.get(self.pos..end).ok_or_else(short)?;
681241535×
        self.pos = end;
691241535×
        Ok(s)
701241538×
    }
71243374×
    pub fn u8(&mut self) -> Result<u8, ObjectError> {
72243374×
        Ok(self.take(1)?[0])
73243374×
    }
74312177×
    pub fn u32(&mut self) -> Result<u32, ObjectError> {
75312177×
        Ok(u32::from_le_bytes(self.fixed::<4>()?))
76312177×
    }
7755368×
    pub fn u64(&mut self) -> Result<u64, ObjectError> {
7855368×
        Ok(u64::from_le_bytes(self.fixed::<8>()?))
7955368×
    }
80
    /// A count; checked against what is left, so a corrupt one cannot ask
81
    /// for more room than the index could hold.
8264805×
    pub fn count(&mut self) -> Result<usize, ObjectError> {
8364805×
        let n = self.u32()? as usize;
8464805×
        if n > self.buf.len() - self.pos {
850×
            return Err(short());
8664805×
        }
8764805×
        Ok(n)
8864805×
    }
89890661×
    pub fn fixed<const N: usize>(&mut self) -> Result<[u8; N], ObjectError> {
90890661×
        Ok(self.take(N)?.try_into().expect("N bytes"))
91890661×
    }
92107503×
    pub fn bytes(&mut self) -> Result<&'a [u8], ObjectError> {
93107503×
        let n = self.u32()? as usize;
94107503×
        self.take(n)
95107503×
    }
9661919×
    pub fn text(&mut self) -> Result<String, ObjectError> {
9761919×
        String::from_utf8(self.bytes()?.to_vec())
9861919×
            .map_err(|_| ObjectError::Schema("index: text is not UTF-8".into()))
9961919×
    }
100179254×
    pub fn object(&mut self) -> Result<ObjectRef, ObjectError> {
101179254×
        Ok(ObjectRef(self.fixed::<32>()?))
102179254×
    }
10326581×
    pub fn opt_object(&mut self) -> Result<Option<ObjectRef>, ObjectError> {
10426581×
        Ok(match self.u8()? {
10526329×
            0 => None,
106252×
            _ => Some(self.object()?),
107
        })
10826581×
    }
109
}
110
111
#[cfg(test)]
112
mod tests {
113
    use super::*;
114
115
    #[test]
1161×
    fn numbers_and_bytes_round_trip_and_truncation_fails() {
1171×
        let mut w = Writer::default();
1181×
        w.u8(7);
1191×
        w.u32(70_000);
1201×
        w.u64(1 << 40);
1211×
        w.bytes(b"path/name");
1221×
        w.opt_object(&Some(ObjectRef([3; 32])));
1231×
        w.opt_object(&None);
1241×
        let mut r = Reader::new(&w.0);
1251×
        assert_eq!(r.u8().unwrap(), 7);
1261×
        assert_eq!(r.u32().unwrap(), 70_000);
1271×
        assert_eq!(r.u64().unwrap(), 1 << 40);
1281×
        assert_eq!(r.text().unwrap(), "path/name");
1291×
        assert_eq!(r.opt_object().unwrap(), Some(ObjectRef([3; 32])));
1301×
        assert_eq!(r.opt_object().unwrap(), None);
1311×
        assert!(r.at_end());
1321×
        let mut r = Reader::new(&w.0[..w.0.len() - 3]);
1331×
        r.fixed::<13>().unwrap();
1341×
        assert!(r.text().is_ok());
1351×
        assert!(r.opt_object().is_err());
1361×
    }
137
}