commonware_storage/qmdb/immutable/operation/
variable.rs1use super::{Operation, COMMIT_CONTEXT, SET_CONTEXT};
2use crate::{
3 merkle::{Family, Location},
4 qmdb::{
5 any::{value::VariableEncoding, VariableValue},
6 operation::Key,
7 },
8};
9use commonware_codec::{varint::UInt, EncodeSize, Error as CodecError, Read, ReadExt as _, Write};
10use commonware_runtime::{Buf, BufMut};
11
12impl<F: Family, K: Key, V: VariableValue> EncodeSize for Operation<F, K, VariableEncoding<V>> {
13 fn encode_size(&self) -> usize {
14 1 + match self {
15 Self::Set(k, v) => k.encode_size() + v.encode_size(),
16 Self::Commit(v, floor) => v.encode_size() + UInt(**floor).encode_size(),
17 }
18 }
19}
20
21impl<F: Family, K: Key, V: VariableValue> Write for Operation<F, K, VariableEncoding<V>> {
22 fn write(&self, buf: &mut impl BufMut) {
23 match &self {
24 Self::Set(k, v) => {
25 SET_CONTEXT.write(buf);
26 k.write(buf);
27 v.write(buf);
28 }
29 Self::Commit(v, floor_loc) => {
30 COMMIT_CONTEXT.write(buf);
31 v.write(buf);
32 UInt(**floor_loc).write(buf);
33 }
34 }
35 }
36}
37
38impl<F: Family, K: Key, V: VariableValue> Read for Operation<F, K, VariableEncoding<V>> {
39 type Cfg = (<K as Read>::Cfg, <V as Read>::Cfg);
40
41 fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, CodecError> {
42 match u8::read(buf)? {
43 SET_CONTEXT => {
44 let key = K::read_cfg(buf, &cfg.0)?;
45 let value = V::read_cfg(buf, &cfg.1)?;
46 Ok(Self::Set(key, value))
47 }
48 COMMIT_CONTEXT => {
49 let metadata = Option::<V>::read_cfg(buf, &cfg.1)?;
50 let floor_loc = Location::read(buf)?;
51 Ok(Self::Commit(metadata, floor_loc))
52 }
53 e => Err(CodecError::InvalidEnum(e)),
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::merkle::mmr;
62 use commonware_codec::{DecodeExt, Encode, EncodeSize, FixedSize as _};
63 use commonware_utils::sequence::U64;
64
65 type VarOp = Operation<mmr::Family, U64, VariableEncoding<U64>>;
66
67 #[test]
68 fn test_operation_encode_decode() {
69 let key = U64::new(1234);
70 let value = U64::new(56789);
71
72 let set_op = VarOp::Set(key, value.clone());
74 let encoded = set_op.encode();
75 let decoded = VarOp::decode(encoded).unwrap();
76 assert_eq!(set_op, decoded);
77
78 let commit_op = VarOp::Commit(Some(value), Location::new(100));
80 let encoded = commit_op.encode();
81 let decoded = VarOp::decode(encoded).unwrap();
82 assert_eq!(commit_op, decoded);
83
84 let commit_op = VarOp::Commit(None, Location::new(0));
86 let encoded = commit_op.encode();
87 let decoded = VarOp::decode(encoded).unwrap();
88 assert_eq!(commit_op, decoded);
89 }
90
91 #[test]
92 fn test_operation_encode_size() {
93 let key = U64::new(1234);
94 let value = U64::new(56789);
95
96 let set_op = VarOp::Set(key, value.clone());
97 assert_eq!(set_op.encode_size(), 1 + U64::SIZE + value.encode_size());
98 assert_eq!(set_op.encode().len(), set_op.encode_size());
99
100 let floor = Location::new(100);
101 let commit_op = VarOp::Commit(Some(value.clone()), floor);
102 assert_eq!(
103 commit_op.encode_size(),
104 1 + Some(value).encode_size() + UInt(*floor).encode_size()
105 );
106 assert_eq!(commit_op.encode().len(), commit_op.encode_size());
107
108 let commit_op = VarOp::Commit(None, Location::new(0));
109 assert_eq!(
110 commit_op.encode_size(),
111 1 + Option::<U64>::None.encode_size() + UInt(0u64).encode_size()
112 );
113 assert_eq!(commit_op.encode().len(), commit_op.encode_size());
114 }
115
116 #[test]
117 fn test_operation_invalid_context() {
118 let invalid = vec![0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0];
119 let decoded = VarOp::decode(invalid.as_ref());
120 assert!(matches!(
121 decoded.unwrap_err(),
122 CodecError::InvalidEnum(0xFF)
123 ));
124 }
125
126 #[test]
127 fn test_operation_insufficient_buffer() {
128 let invalid = vec![SET_CONTEXT];
129 let decoded = VarOp::decode(invalid.as_ref());
130 assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer));
131
132 let invalid = vec![COMMIT_CONTEXT];
133 let decoded = VarOp::decode(invalid.as_ref());
134 assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer));
135 }
136
137 #[test]
138 fn test_operation_roundtrip_all_variants() {
139 let key = U64::new(100);
140 let value = U64::new(1000);
141
142 let operations: Vec<VarOp> = vec![
143 VarOp::Set(key, value.clone()),
144 VarOp::Commit(Some(value), Location::new(50)),
145 VarOp::Commit(None, Location::new(0)),
146 ];
147
148 for op in operations {
149 let encoded = op.encode();
150 let decoded = VarOp::decode(encoded.clone()).unwrap();
151 assert_eq!(op, decoded, "Failed to roundtrip: {op:?}");
152 assert_eq!(encoded.len(), op.encode_size(), "Size mismatch for: {op:?}");
153 }
154 }
155
156 #[test]
157 fn test_operation_variable_key_roundtrip() {
158 use commonware_codec::Decode as _;
159
160 type VecOp = Operation<mmr::Family, Vec<u8>, VariableEncoding<U64>>;
161
162 let key = vec![1u8, 2, 3, 4, 5];
163 let cfg = ((commonware_codec::RangeCfg::from(0..=100usize), ()), ());
164
165 let set_op = VecOp::Set(key, U64::new(42));
167 let encoded = set_op.encode();
168 assert_eq!(encoded.len(), set_op.encode_size());
169 let decoded = VecOp::decode_cfg(encoded, &cfg).unwrap();
170 assert_eq!(set_op, decoded);
171
172 let commit_op = VecOp::Commit(Some(U64::new(42)), Location::new(10));
174 let encoded = commit_op.encode();
175 let decoded = VecOp::decode_cfg(encoded, &cfg).unwrap();
176 assert_eq!(commit_op, decoded);
177
178 let empty_key_op = VecOp::Set(vec![], U64::new(99));
180 let encoded = empty_key_op.encode();
181 let decoded = VecOp::decode_cfg(encoded, &cfg).unwrap();
182 assert_eq!(empty_key_op, decoded);
183 }
184
185 #[cfg(feature = "arbitrary")]
186 mod conformance {
187 use super::*;
188 use commonware_codec::conformance::CodecConformance;
189
190 type VarKeyOp = Operation<mmr::Family, Vec<u8>, VariableEncoding<U64>>;
191
192 commonware_conformance::conformance_tests! {
193 CodecConformance<VarOp>,
194 CodecConformance<VarKeyOp>
195 }
196 }
197}