Skip to main content

commonware_storage/qmdb/immutable/operation/
variable.rs

1use 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        // Test Set operation
64        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        // Test Commit operation with value
70        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        // Test Commit operation without value
76        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        // Test Set with variable-length key
151        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        // Test Commit (key-independent, should work the same)
159        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        // Test empty key
166        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}