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 ObjectEntry {
92    fn memory_size(&self) -> usize {
93        std::mem::size_of::<Self>()
94            .saturating_add(self.position.memory_size().saturating_sub(std::mem::size_of::<StorageCursor>()))
95            .saturating_add(self.version.memory_size().saturating_sub(std::mem::size_of::<ObjectVersionToken>()))
96            .saturating_add(self.value.memory_size().saturating_sub(std::mem::size_of::<ObjectValue>()))
97    }
98}
99
100impl<T: MemorySize> MemorySize for ObjectRevision<T> {
101    fn memory_size(&self) -> usize {
102        std::mem::size_of::<Self>()
103            .saturating_add(self.position.memory_size().saturating_sub(std::mem::size_of::<StorageCursor>()))
104            .saturating_add(self.version.memory_size().saturating_sub(std::mem::size_of::<ObjectVersionToken>()))
105            .saturating_add(self.value.memory_size().saturating_sub(std::mem::size_of::<T>()))
106    }
107}
108
109impl MemorySize for ObjectReceipt {
110    fn memory_size(&self) -> usize {
111        std::mem::size_of::<Self>() + self.position.memory_size() - std::mem::size_of::<StorageCursor>() + self.version.memory_size()
112            - std::mem::size_of::<ObjectVersionToken>()
113    }
114}
115
116impl MemorySize for ObjectVersionToken {
117    fn memory_size(&self) -> usize {
118        std::mem::size_of::<Self>().saturating_add(self.history.memory_size().saturating_sub(std::mem::size_of::<StoreHistory>()))
119    }
120}
121
122impl ObjectVersionToken {
123    pub fn history(&self) -> &StoreHistory {
124        &self.history
125    }
126
127    pub fn revision(&self) -> NonZeroU64 {
128        self.revision
129    }
130
131    pub fn encode_bounded(&self, max_bytes: NonZeroUsize) -> Result<Vec<u8>> {
132        if self.encoded_len() > max_bytes.get() {
133            return Err(invalid("object version encoding exceeds allowance"));
134        }
135        Ok(self.encode_to_vec())
136    }
137
138    pub fn decode_bounded(bytes: &[u8], max_bytes: NonZeroUsize) -> Result<Self> {
139        let required = Self::decoding_memory(bytes).map_err(|_| invalid("invalid object version encoding"))?;
140        if required > max_bytes.get() {
141            return Err(invalid("object version decoding exceeds allowance"));
142        }
143        let token = Self::decode(bytes).map_err(|_| invalid("invalid object version encoding"))?;
144        if token.memory_size() > max_bytes.get() {
145            return Err(invalid("object version decoding exceeds allowance"));
146        }
147        Ok(token)
148    }
149
150    pub fn new(position: StorageCursor) -> Self {
151        Self { history: position.history, revision: NonZeroU64::new(position.sequence).expect("admission positions are nonzero") }
152    }
153
154    pub fn check_position(&self, position: &StorageCursor) -> Result<()> {
155        self.history.check(&position.history)?;
156        if self.revision.get() != position.sequence {
157            return Err(invalid("object version does not match its admission identity"));
158        }
159        Ok(())
160    }
161}
162
163crate::postcard_product!(ObjectVersionToken { history: StoreHistory, revision: NonZeroU64 });
164
165impl Described for ObjectVersionToken {
166    const NAME: &'static str = concat!(module_path!(), "::", stringify!(ObjectVersionToken));
167    const FIELDS: &'static [Field] = &[
168        Field { name: Cow::Borrowed("store"), tag: 1, kind: Kind::String, cardinality: Cardinality::Required },
169        Field { name: Cow::Borrowed("epoch"), tag: 2, kind: Kind::String, cardinality: Cardinality::Required },
170        Field { name: Cow::Borrowed("revision"), tag: 3, kind: Kind::U64, cardinality: Cardinality::Required },
171    ];
172
173    const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
174        { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
175    >(Self::NAME, Self::FIELDS);
176}
177
178/* === Public Functions === */
179
180/* === Private Functions === */
181
182/* === Tests === */