Skip to main content

heddle_object_model/compact/
extract.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use super::{CompactError, Result, decode_state_frame};
4use crate::object::{State, StateId};
5
6/// Reconstruct one state and verify its BLAKE3 typed identity.
7///
8/// The whole-frame checksum is verified, every fidelity column is decoded, and
9/// the state's id is recomputed from the reconstructed fields before it is
10/// compared with `expected`.
11pub fn extract_state(bytes: &[u8], expected: StateId) -> Result<State> {
12    decode_state_frame(bytes)?
13        .into_iter()
14        .find(|state| state.accepts_stored_id(&expected))
15        .map(|mut state| {
16            state.state_id = expected;
17            state
18        })
19        .ok_or(CompactError::Missing)
20}
21
22#[cfg(test)]
23mod tests {
24    use std::time::Instant;
25
26    use super::super::{decode_tree_frame, encode_tree_frame, extract_tree};
27    use crate::object::{ContentHash, Tree, TreeEntry};
28
29    #[test]
30    fn extract_tree_point_read_beats_decode_all_and_serialize() {
31        let trees = (0..64)
32            .map(|index| {
33                Tree::from_entries(vec![
34                    TreeEntry::file(
35                        format!("file-{index}.txt"),
36                        ContentHash::from_bytes([index as u8; 32]),
37                        false,
38                    )
39                    .unwrap(),
40                ])
41            })
42            .collect::<Vec<_>>();
43        let encoded = encode_tree_frame(&trees).unwrap();
44        let first = trees[0].hash();
45        let last = trees[trees.len() - 1].hash();
46        let _ = extract_tree(&encoded, first).unwrap();
47        let _ = decode_tree_frame(&encoded).unwrap();
48
49        const ITERATIONS: u32 = 200;
50        let start = Instant::now();
51        for _ in 0..ITERATIONS {
52            let decoded = decode_tree_frame(&encoded).unwrap();
53            let _ = decoded
54                .into_iter()
55                .map(|tree| rmp_serde::to_vec_named(&tree).unwrap())
56                .collect::<Vec<_>>();
57        }
58        let decode_all = start.elapsed();
59
60        let start = Instant::now();
61        for _ in 0..ITERATIONS {
62            let tree = extract_tree(&encoded, first).unwrap();
63            let _ = rmp_serde::to_vec_named(&tree).unwrap();
64        }
65        let extract_first = start.elapsed();
66
67        let start = Instant::now();
68        for _ in 0..ITERATIONS {
69            let tree = extract_tree(&encoded, last).unwrap();
70            let _ = rmp_serde::to_vec_named(&tree).unwrap();
71        }
72        let extract_last = start.elapsed();
73
74        eprintln!(
75            "compact tree frame (64 trees, {ITERATIONS} iters): decode-all+serialize-all {decode_all:?}; extract-first {extract_first:?}; extract-last {extract_last:?}"
76        );
77        assert!(
78            extract_first < decode_all && extract_last < decode_all,
79            "single-object extract should beat decode-all, first={extract_first:?} last={extract_last:?} decode_all={decode_all:?}"
80        );
81    }
82}