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