rustdb/
bytes.rs

1use crate::{util, Cell, Ordering, Rc, Record, SaveOp, SortedFile, DB};
2
3/// Number of fragment types.
4pub const NFT: usize = 4;
5
6/// Total bytes used taking into account all overhead ( 3 + 1 + 8 = 12 bytes, per fragment ).
7fn tot(len: usize, bpf: usize) -> usize {
8    let nf = (len + bpf - 1) / bpf;
9    nf * (bpf + 12)
10}
11
12/// Calculate best fragment type from byte length.
13pub fn fragment_type(len: usize, bpf: &[usize]) -> usize {
14    let mut best = usize::MAX;
15    let mut result = 0;
16    for (ft, bpf) in bpf.iter().enumerate() {
17        let t = tot(len, *bpf);
18        if t <= best {
19            best = t;
20            result = ft;
21        }
22    }
23    result
24}
25
26/// Calculate fragment sizes.
27pub fn bpf(hp: usize) -> [usize; NFT] {
28    let hp = hp - 8; // 8 is to account for page header.
29    let pp = hp / 1000;
30    let max_bpf = hp / pp - 12;
31    [40, 127, 333, max_bpf]
32}
33
34/// Storage of variable size values.
35pub struct ByteStorage {
36    /// File for storing fragments.
37    pub file: Rc<SortedFile>,
38    id_gen: Cell<u64>,
39    /// Bytes per fragment.
40    bpf: usize,
41}
42
43impl ByteStorage {
44    /// Construct new ByteStorage with specified root page and fragment type.
45    pub fn new(root_page: u64, bpf: usize) -> Self {
46        let file = Rc::new(SortedFile::new(9 + bpf, 8, root_page));
47        ByteStorage {
48            file,
49            id_gen: Cell::new(u64::MAX),
50            bpf,
51        }
52    }
53
54    /// Get fragment Id value.
55    fn get_id(&self, db: &DB) -> u64 {
56        let mut result = self.id_gen.get();
57        if result == u64::MAX {
58            result = 0;
59            // Initialise id_gen to id of last record.
60            let start = Fragment::new(u64::MAX, self.bpf);
61            if let Some((pp, off)) = self.file.clone().dsc(db, Box::new(start)).next() {
62                let p = pp.borrow();
63                result = 1 + util::getu64(&p.data, off);
64            }
65            self.id_gen.set(result);
66        }
67        result
68    }
69
70    /// Check whether there are changes to underlying file.
71    pub fn changed(&self) -> bool {
72        self.file.changed()
73    }
74
75    /// Save to underlying file.
76    pub fn save(&self, db: &DB, op: SaveOp) {
77        self.file.save(db, op);
78    }
79
80    /// Encode bytes.
81    pub fn encode(&self, db: &DB, bytes: &[u8]) -> u64 {
82        let result = self.get_id(db);
83        let mut r = Fragment::new(0, self.bpf);
84        let n = bytes.len();
85        let mut done = 0;
86        loop {
87            r.id = self.id_gen.get();
88            self.id_gen.set(r.id + 1);
89            let mut len = n - done;
90            if len > self.bpf {
91                r.last = false;
92                len = self.bpf;
93            } else {
94                r.last = true;
95            }
96            r.len = len;
97            r.bytes[..len].copy_from_slice(&bytes[done..(len + done)]);
98            done += len;
99            self.file.insert(db, &r);
100            if done == n {
101                break;
102            }
103        }
104        result
105    }
106
107    /// Decode bytes, inline bytes are reserved.
108    pub fn decode(&self, db: &DB, mut id: u64, inline: usize) -> Vec<u8> {
109        let mut result = vec![0_u8; inline];
110        let start = Fragment::new(id, self.bpf);
111        for (pp, off) in self.file.asc(db, Box::new(start)) {
112            let p = pp.borrow();
113            let data = &p.data;
114            debug_assert!(util::getu64(data, off) == id);
115            id += 1;
116            let off = off + 8;
117            let (len, last) = decode(&data[off..], self.bpf);
118            result.extend_from_slice(&data[off..off + len]);
119            if last {
120                break;
121            }
122        }
123        result
124    }
125
126    /// Delete a code.
127    pub fn delcode(&self, db: &DB, id: u64) {
128        let start = Fragment::new(id, self.bpf);
129        let mut n = 0;
130        for (pp, off) in self.file.asc(db, Box::new(start)) {
131            let p = pp.borrow();
132            debug_assert!(util::getu64(&p.data, off) == id + n);
133            n += 1;
134            let off = off + 8;
135            let (_len, last) = decode(&p.data[off..], self.bpf);
136            if last {
137                break;
138            }
139        }
140        let mut r = Fragment::new(0, self.bpf);
141        for xid in id..id + n {
142            r.id = xid;
143            self.file.remove(db, &r);
144        }
145    }
146
147    /// Pack underlying file.
148    #[cfg(feature = "pack")]
149    pub fn repack_file(&self, db: &DB) -> i64 {
150        let r = Fragment::new(0, self.bpf);
151        self.file.repack(db, &r)
152    }
153}
154
155/// Values are split into fragments.
156struct Fragment {
157    id: u64,
158    len: usize,
159    last: bool,
160    bytes: Vec<u8>,
161}
162
163impl Fragment {
164    pub fn new(id: u64, bpf: usize) -> Self {
165        Fragment {
166            id,
167            len: 0,
168            last: false,
169            bytes: vec![0; bpf],
170        }
171    }
172}
173
174impl Record for Fragment {
175    fn compare(&self, _db: &DB, data: &[u8]) -> Ordering {
176        let val = util::getu64(data, 0);
177        self.id.cmp(&val)
178    }
179
180    fn save(&self, data: &mut [u8]) {
181        util::setu64(data, self.id);
182        let bpf = self.bytes.len();
183        data[8..8 + self.len].copy_from_slice(&self.bytes[..self.len]);
184
185        // Maybe should zero unused bytes.
186
187        let unused = bpf - self.len;
188        data[8 + bpf] = (unused % 64) as u8
189            + if self.last { 64 } else { 0 }
190            + if unused >= 64 { 128 } else { 0 };
191        if unused >= 64 {
192            data[8 + bpf - 1] = (unused / 64) as u8;
193        }
194    }
195}
196
197/// Result is data length and last flag.
198fn decode(data: &[u8], bpf: usize) -> (usize, bool) {
199    let b = data[bpf];
200    let unused = (b % 64) as usize
201        + if b >= 128 {
202            data[bpf - 1] as usize * 64
203        } else {
204            0
205        };
206    (bpf - unused, b & 64 != 0)
207}