commonware_storage/qmdb/any/operation/
fixed.rs1use 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
19const 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
124impl<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
135impl<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}