Skip to main content

commonware_storage/qmdb/any/operation/
fixed.rs

1use crate::{
2    merkle::{Family, Location},
3    qmdb::any::{
4        operation::{
5            update, Operation, OperationCodec, Update, COMMIT_CONTEXT, DELETE_CONTEXT,
6            UPDATE_CONTEXT,
7        },
8        value::FixedEncoding,
9        FixedValue,
10    },
11};
12use commonware_codec::{
13    util::{at_least, ensure_zeros},
14    Codec, CodecFixed, Error as CodecError, FixedSize, ReadExt as _, Write,
15};
16use commonware_runtime::{Buf, BufMut};
17use commonware_utils::Array;
18
19/// `max(a, b)` in a const context.
20const fn const_max(a: usize, b: usize) -> usize {
21    if a > b {
22        a
23    } else {
24        b
25    }
26}
27
28const fn update_op_size<S: FixedSize>() -> usize {
29    1 + S::SIZE
30}
31
32const fn commit_op_size<V: FixedSize>() -> usize {
33    1 + 1 + V::SIZE + u64::SIZE
34}
35
36const fn delete_op_size<K: Array>() -> usize {
37    1 + K::SIZE
38}
39
40const fn total_op_size<K: Array, V: FixedSize, S: FixedSize>() -> usize {
41    const_max(
42        update_op_size::<S>(),
43        const_max(commit_op_size::<V>(), delete_op_size::<K>()),
44    )
45}
46
47impl<F, V, S> OperationCodec<F, S> for FixedEncoding<V>
48where
49    F: Family,
50    S::Key: Array + Codec,
51    V: FixedValue,
52    S: Update<Value = V, ValueEncoding = Self> + CodecFixed<Cfg = ()>,
53{
54    type ReadCfg = ();
55
56    fn write_operation(op: &Operation<F, S>, buf: &mut impl BufMut) {
57        let total = total_op_size::<S::Key, V, S>();
58        match op {
59            Operation::Delete(k) => {
60                DELETE_CONTEXT.write(buf);
61                k.write(buf);
62                buf.put_bytes(0, total - delete_op_size::<S::Key>());
63            }
64            Operation::Update(p) => {
65                UPDATE_CONTEXT.write(buf);
66                p.write(buf);
67                buf.put_bytes(0, total - update_op_size::<S>());
68            }
69            Operation::CommitFloor(metadata, floor_loc) => {
70                COMMIT_CONTEXT.write(buf);
71                if let Some(metadata) = metadata {
72                    true.write(buf);
73                    metadata.write(buf);
74                } else {
75                    buf.put_bytes(0, V::SIZE + 1);
76                }
77                buf.put_slice(&floor_loc.to_be_bytes());
78                buf.put_bytes(0, total - commit_op_size::<V>());
79            }
80        }
81    }
82
83    fn read_operation(
84        buf: &mut impl Buf,
85        cfg: &Self::ReadCfg,
86    ) -> Result<Operation<F, S>, CodecError> {
87        let total = total_op_size::<S::Key, V, S>();
88        at_least(buf, total)?;
89
90        match u8::read(buf)? {
91            DELETE_CONTEXT => {
92                let key = S::Key::read(buf)?;
93                ensure_zeros(buf, total - delete_op_size::<S::Key>())?;
94                Ok(Operation::Delete(key))
95            }
96            UPDATE_CONTEXT => {
97                let payload = S::read_cfg(buf, cfg)?;
98                ensure_zeros(buf, total - update_op_size::<S>())?;
99                Ok(Operation::Update(payload))
100            }
101            COMMIT_CONTEXT => {
102                let is_some = bool::read(buf)?;
103                let metadata = if is_some {
104                    Some(V::read_cfg(buf, cfg)?)
105                } else {
106                    ensure_zeros(buf, V::SIZE)?;
107                    None
108                };
109                let floor_loc = Location::new(u64::read(buf)?);
110                if !floor_loc.is_valid() {
111                    return Err(CodecError::Invalid(
112                        "storage::qmdb::any::operation::fixed::Operation",
113                        "commit floor location overflow",
114                    ));
115                }
116                ensure_zeros(buf, total - commit_op_size::<V>())?;
117                Ok(Operation::CommitFloor(metadata, floor_loc))
118            }
119            e => Err(CodecError::InvalidEnum(e)),
120        }
121    }
122}
123
124// FixedSize for ordered fixed operations.
125impl<F, K, V> FixedSize for Operation<F, update::Ordered<K, FixedEncoding<V>>>
126where
127    F: Family,
128    K: Array,
129    V: FixedValue,
130    update::Ordered<K, FixedEncoding<V>>: FixedSize,
131{
132    const SIZE: usize = total_op_size::<K, V, update::Ordered<K, FixedEncoding<V>>>();
133}
134
135// FixedSize for unordered fixed operations.
136impl<F, K, V> FixedSize for Operation<F, update::Unordered<K, FixedEncoding<V>>>
137where
138    F: Family,
139    K: Array,
140    V: FixedValue,
141    update::Unordered<K, FixedEncoding<V>>: FixedSize,
142{
143    const SIZE: usize = total_op_size::<K, V, update::Unordered<K, FixedEncoding<V>>>();
144}