Skip to main content

harmos_domain/model/storage/
objects.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12pub mod codec;
13
14/* === Re-Exports === */
15
16pub use codec::Key;
17
18/* === Imports === */
19
20// Standard Library Imports
21use std::borrow::Cow;
22use std::num::{NonZeroU64, NonZeroUsize};
23
24// Third Party Imports
25use serde::{Deserialize, Serialize};
26
27// Local Imports
28use crate::Identifier;
29use crate::model::storage::keys::invalid;
30use crate::model::storage::{ObjectKey, ObjectValue, StorageCursor, StoreHistory};
31use crate::schema::declaration::{Field, Kind};
32use crate::schema::{Cardinality, Described};
33use crate::{MemorySize, Message, Result};
34
35/* === Constants === */
36
37/* === Macros === */
38
39/* === Type Aliases === */
40
41/* === Structs === */
42
43#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(deny_unknown_fields)]
45pub struct ObjectEntry {
46    pub position: StorageCursor,
47    pub version: ObjectVersionToken,
48    pub value: ObjectValue,
49}
50
51#[derive(Clone, Debug)]
52pub struct ObjectRevision<T> {
53    pub position: StorageCursor,
54    pub version: ObjectVersionToken,
55    pub value: T,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(deny_unknown_fields)]
60pub struct ObjectPoint {
61    pub schema: Identifier,
62    pub item: ObjectKey,
63    pub version: ObjectVersion,
64}
65
66#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
67#[serde(deny_unknown_fields)]
68pub struct ObjectVersionToken {
69    pub(crate) history: StoreHistory,
70    pub(crate) revision: NonZeroU64,
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
74pub struct ObjectReceipt {
75    pub position: StorageCursor,
76    pub version: ObjectVersionToken,
77}
78
79/* === Enums === */
80
81#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
82pub enum ObjectVersion {
83    Latest,
84    Exact(ObjectVersionToken),
85}
86
87/* === Traits === */
88
89/* === Implementations === */
90
91impl MemorySize for ObjectPoint {
92    fn memory_size(&self) -> usize {
93        std::mem::size_of::<Self>()
94            .saturating_add(self.schema.memory_size().saturating_sub(std::mem::size_of::<Identifier>()))
95            .saturating_add(self.item.memory_size().saturating_sub(std::mem::size_of::<ObjectKey>()))
96            .saturating_add(match &self.version {
97                ObjectVersion::Latest => 0,
98                ObjectVersion::Exact(token) => token.memory_size().saturating_sub(std::mem::size_of::<ObjectVersionToken>()),
99            })
100    }
101}
102
103impl MemorySize for ObjectEntry {
104    fn memory_size(&self) -> usize {
105        std::mem::size_of::<Self>()
106            .saturating_add(self.position.memory_size().saturating_sub(std::mem::size_of::<StorageCursor>()))
107            .saturating_add(self.version.memory_size().saturating_sub(std::mem::size_of::<ObjectVersionToken>()))
108            .saturating_add(self.value.memory_size().saturating_sub(std::mem::size_of::<ObjectValue>()))
109    }
110}
111
112impl<T: MemorySize> MemorySize for ObjectRevision<T> {
113    fn memory_size(&self) -> usize {
114        std::mem::size_of::<Self>()
115            .saturating_add(self.position.memory_size().saturating_sub(std::mem::size_of::<StorageCursor>()))
116            .saturating_add(self.version.memory_size().saturating_sub(std::mem::size_of::<ObjectVersionToken>()))
117            .saturating_add(self.value.memory_size().saturating_sub(std::mem::size_of::<T>()))
118    }
119}
120
121impl MemorySize for ObjectReceipt {
122    fn memory_size(&self) -> usize {
123        std::mem::size_of::<Self>() + self.position.memory_size() - std::mem::size_of::<StorageCursor>() + self.version.memory_size()
124            - std::mem::size_of::<ObjectVersionToken>()
125    }
126}
127
128impl MemorySize for ObjectVersionToken {
129    fn memory_size(&self) -> usize {
130        std::mem::size_of::<Self>().saturating_add(self.history.memory_size().saturating_sub(std::mem::size_of::<StoreHistory>()))
131    }
132}
133
134impl ObjectVersionToken {
135    pub fn history(&self) -> &StoreHistory {
136        &self.history
137    }
138
139    pub fn revision(&self) -> NonZeroU64 {
140        self.revision
141    }
142
143    pub fn encode_bounded(&self, max_bytes: NonZeroUsize) -> Result<Vec<u8>> {
144        if self.encoded_len() > max_bytes.get() {
145            return Err(invalid("object version encoding exceeds allowance"));
146        }
147        Ok(self.encode_to_vec())
148    }
149
150    pub fn decode_bounded(bytes: &[u8], max_bytes: NonZeroUsize) -> Result<Self> {
151        let required = Self::decoding_memory(bytes).map_err(|_| invalid("invalid object version encoding"))?;
152        if required > max_bytes.get() {
153            return Err(invalid("object version decoding exceeds allowance"));
154        }
155        let token = Self::decode(bytes).map_err(|_| invalid("invalid object version encoding"))?;
156        if token.memory_size() > max_bytes.get() {
157            return Err(invalid("object version decoding exceeds allowance"));
158        }
159        Ok(token)
160    }
161
162    pub fn new(position: StorageCursor) -> Self {
163        Self { history: position.history, revision: NonZeroU64::new(position.sequence).expect("admission positions are nonzero") }
164    }
165
166    pub fn check_position(&self, position: &StorageCursor) -> Result<()> {
167        self.history.check(&position.history)?;
168        if self.revision.get() != position.sequence {
169            return Err(invalid("object version does not match its admission identity"));
170        }
171        Ok(())
172    }
173}
174
175crate::postcard_product!(ObjectVersionToken { history: StoreHistory, revision: NonZeroU64 });
176
177impl Described for ObjectVersionToken {
178    const NAME: &'static str = concat!(module_path!(), "::", stringify!(ObjectVersionToken));
179    const FIELDS: &'static [Field] = &[
180        Field { name: Cow::Borrowed("store"), tag: 1, kind: Kind::String, cardinality: Cardinality::Required },
181        Field { name: Cow::Borrowed("epoch"), tag: 2, kind: Kind::String, cardinality: Cardinality::Required },
182        Field { name: Cow::Borrowed("revision"), tag: 3, kind: Kind::U64, cardinality: Cardinality::Required },
183    ];
184
185    const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
186        { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
187    >(Self::NAME, Self::FIELDS);
188}
189
190/* === Public Functions === */
191
192/* === Private Functions === */
193
194/* === Tests === */