harmos_storage/state/
mod.rs1mod admission;
13pub mod config;
14pub mod file;
15mod reader;
16
17pub use admission::Permit;
20pub use config::StorageStateSettings;
21pub use reader::RecoveryReader;
22
23use serde::{Deserialize, Serialize};
29use super::{ReadLimits, RecoveryPosition, StorageFuture, StoreHistory};
31use crate::{Error, Result};
32
33#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(deny_unknown_fields)]
43pub struct RecoveryEntry {
44 pub required_data: super::StorageCursor,
45 pub position: RecoveryPosition,
46
47 #[serde(with = "payload")]
48 pub payload: Vec<u8>,
49}
50
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(deny_unknown_fields)]
53pub struct RecoverySnapshot {
54 pub required_data: super::StorageCursor,
55 pub through: RecoveryPosition,
56
57 #[serde(with = "payload")]
58 pub payload: Vec<u8>,
59}
60
61#[derive(Clone, Debug, PartialEq, Eq)]
62pub struct RecoveryPage {
63 pub entries: Vec<RecoveryEntry>,
64 pub next: Option<RecoveryPosition>,
65}
66
67pub trait StateBackend: Send + Sync + 'static {
72 fn history(&self) -> &StoreHistory;
73 fn open(&self) -> StorageFuture<'_, RecoveryPosition>;
74 fn read_snapshot(&self, max_bytes: std::num::NonZeroUsize) -> StorageFuture<'_, Option<RecoverySnapshot>>;
75 fn write_snapshot<'a>(&'a self, snapshot: &'a RecoverySnapshot) -> StorageFuture<'a, ()>;
76 fn read_entries<'a>(
77 &'a self, after: &'a RecoveryPosition, through: &'a RecoveryPosition, limits: ReadLimits,
78 ) -> StorageFuture<'a, RecoveryPage>;
79 fn write_entries<'a>(&'a self, entries: &'a [RecoveryEntry]) -> StorageFuture<'a, ()>;
80 fn flush<'a>(&'a self, through: &'a RecoveryPosition) -> StorageFuture<'a, RecoveryPosition>;
81 fn close(&self) -> StorageFuture<'_, ()>;
82}
83
84impl RecoveryPage {
87 pub fn check(
88 &self, after: &RecoveryPosition, through: &RecoveryPosition, limits: ReadLimits,
89 mut encoded_bytes: impl FnMut(&RecoveryEntry) -> usize,
90 ) -> Result<()> {
91 if !through.covers(after)? {
92 return Err(Error::InvalidInput { field: "recovery range", reason: "start exceeds fixed frontier" });
93 }
94 let mut previous = after.sequence;
95 let mut bytes = 0usize;
96
97 for entry in &self.entries {
98 after.history.check(&entry.position.history)?;
99 let expected = previous.checked_add(1).ok_or(Error::InvalidInput { field: "recovery page", reason: "position overflow" })?;
100 if entry.position.sequence < expected {
101 return Err(Error::InvalidInput { field: "recovery page", reason: "duplicate or reversed position" });
102 }
103 if entry.position.sequence > expected {
104 return Err(Error::Gap { requested: expected, available: entry.position.sequence });
105 }
106 if entry.position.sequence > through.sequence {
107 return Err(Error::InvalidInput { field: "recovery page", reason: "entry exceeds fixed frontier" });
108 }
109 bytes = bytes
110 .checked_add(encoded_bytes(entry))
111 .ok_or(Error::InvalidInput { field: "recovery page", reason: "encoded size overflow" })?;
112 previous = entry.position.sequence;
113 }
114 limits.check(self.entries.len(), bytes)?;
115
116 let expected_next =
117 if previous < through.sequence { Some(RecoveryPosition { history: after.history.clone(), sequence: previous }) } else { None };
118 if self.next != expected_next || (self.entries.is_empty() && expected_next.is_some()) {
119 return Err(Error::InvalidInput { field: "recovery page", reason: "missing progress or invalid continuation" });
120 }
121
122 Ok(())
123 }
124}
125
126mod payload {
132 use base64::Engine;
133 use base64::engine::general_purpose::STANDARD_NO_PAD;
134 use serde::{Deserialize, Deserializer, Serializer, de::Error};
135
136 pub(super) fn serialize<S: Serializer>(payload: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
137 serializer.serialize_str(&STANDARD_NO_PAD.encode(payload))
138 }
139
140 pub(super) fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
141 let encoded = String::deserialize(deserializer)?;
142 STANDARD_NO_PAD.decode(encoded.as_bytes()).map_err(D::Error::custom)
143 }
144}
145
146