Skip to main content

commonware_storage/qmdb/any/operation/
fixed.rs

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