cu29_logstream/
recovery.rs1use crate::{DecodedRecord, Error, RecordKind, Result, decode_record, encode_record};
4use alloc::{string::ToString, vec::Vec};
5use bincode::{Decode, Encode};
6use cu29_runtime::curuntime::KeyFrame;
7
8#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
14pub struct RecoveryPoint {
15 pub manifest_object_id: u64,
16 pub manifest_record_digest: [u8; 32],
17 pub copperlist_id: u64,
18 pub keyframe_object_id: u64,
19 pub keyframe_record_digest: [u8; 32],
20}
21
22impl RecoveryPoint {
23 pub fn references_keyframe(&self, record: DecodedRecord<'_>) -> bool {
25 record.kind == RecordKind::KeyFrame
26 && record.object_id == self.keyframe_object_id
27 && record.digest == self.keyframe_record_digest
28 }
29
30 pub fn references_manifest(&self, record: DecodedRecord<'_>) -> bool {
32 record.kind == RecordKind::Manifest
33 && record.object_id == self.manifest_object_id
34 && record.digest == self.manifest_record_digest
35 }
36}
37
38pub fn encode_keyframe(keyframe: &KeyFrame) -> Result<Vec<u8>> {
40 bincode::encode_to_vec(keyframe, bincode::config::standard())
41 .map_err(|error| Error::Codec(error.to_string()))
42}
43
44pub fn decode_keyframe(payload: &[u8]) -> Result<KeyFrame> {
46 decode_exact(payload, "keyframe")
47}
48
49pub fn encode_recovery_point(recovery_point: &RecoveryPoint) -> Result<Vec<u8>> {
51 bincode::encode_to_vec(recovery_point, bincode::config::standard())
52 .map_err(|error| Error::Codec(error.to_string()))
53}
54
55pub fn decode_recovery_point(payload: &[u8]) -> Result<RecoveryPoint> {
57 decode_exact(payload, "recovery point")
58}
59
60pub fn encode_keyframe_and_recovery_point(
64 keyframe: &KeyFrame,
65 manifest_object_id: u64,
66 manifest_record_digest: [u8; 32],
67) -> Result<(Vec<u8>, Vec<u8>)> {
68 let keyframe_payload = encode_keyframe(keyframe)?;
69 let keyframe_record =
70 encode_record(RecordKind::KeyFrame, keyframe.culistid, &keyframe_payload)?;
71 let keyframe_decoded = decode_record(&keyframe_record)?;
72 let recovery_point = RecoveryPoint {
73 manifest_object_id,
74 manifest_record_digest,
75 copperlist_id: keyframe.culistid,
76 keyframe_object_id: keyframe.culistid,
77 keyframe_record_digest: keyframe_decoded.digest,
78 };
79 let recovery_point_payload = encode_recovery_point(&recovery_point)?;
80 let recovery_point_record = encode_record(
81 RecordKind::RecoveryPoint,
82 keyframe.culistid,
83 &recovery_point_payload,
84 )?;
85 Ok((keyframe_record, recovery_point_record))
86}
87
88fn decode_exact<T: Decode<()>>(payload: &[u8], name: &'static str) -> Result<T> {
89 let (value, consumed) = bincode::decode_from_slice(payload, bincode::config::standard())
90 .map_err(|error| Error::Codec(error.to_string()))?;
91 if consumed != payload.len() {
92 return Err(Error::Codec(alloc::format!("{name} has trailing bytes")));
93 }
94 Ok(value)
95}
96
97pub(crate) fn keyframe_id(payload: &[u8]) -> Result<u64> {
99 #[derive(bincode::BorrowDecode)]
100 struct View<'a> {
101 culistid: u64,
102 _timestamp: cu29_clock::CuTime,
103 _tasks: &'a [u8],
104 }
105 let (view, used): (View<'_>, usize) =
106 bincode::borrow_decode_from_slice(payload, bincode::config::standard())
107 .map_err(|e| Error::Codec(e.to_string()))?;
108 if used != payload.len() {
109 return Err(Error::Codec("keyframe has trailing bytes".into()));
110 }
111 Ok(view.culistid)
112}