use crate::{ merkle::{Family, Location}, qmdb::any::{ operation::{ update, Operation, OperationCodec, Update, COMMIT_CONTEXT, DELETE_CONTEXT, UPDATE_CONTEXT, }, value::FixedEncoding, FixedValue, }, }; use commonware_codec::{ util::{at_least, ensure_zeros}, Codec, CodecFixed, Error as CodecError, FixedSize, ReadExt as _, Write, }; use commonware_runtime::{Buf, BufMut}; use commonware_utils::Array; /// `max(a, b)` in a const context. const fn const_max(a: usize, b: usize) -> usize { if a > b { a } else { b } } const fn update_op_size() -> usize { 1 + S::SIZE } const fn commit_op_size() -> usize { 1 + 1 + V::SIZE + u64::SIZE } const fn delete_op_size() -> usize { 1 + K::SIZE } const fn total_op_size() -> usize { const_max( update_op_size::(), const_max(commit_op_size::(), delete_op_size::()), ) } impl OperationCodec for FixedEncoding where F: Family, S::Key: Array + Codec, V: FixedValue, S: Update + CodecFixed, { type ReadCfg = (); fn write_operation(op: &Operation, buf: &mut impl BufMut) { let total = total_op_size::(); match op { Operation::Delete(k) => { DELETE_CONTEXT.write(buf); k.write(buf); buf.put_bytes(0, total - delete_op_size::()); } Operation::Update(p) => { UPDATE_CONTEXT.write(buf); p.write(buf); buf.put_bytes(0, total - update_op_size::()); } Operation::CommitFloor(metadata, floor_loc) => { COMMIT_CONTEXT.write(buf); if let Some(metadata) = metadata { true.write(buf); metadata.write(buf); } else { buf.put_bytes(0, V::SIZE + 1); } buf.put_slice(&floor_loc.to_be_bytes()); buf.put_bytes(0, total - commit_op_size::()); } } } fn read_operation( buf: &mut impl Buf, cfg: &Self::ReadCfg, ) -> Result, CodecError> { let total = total_op_size::(); at_least(buf, total)?; match u8::read(buf)? { DELETE_CONTEXT => { let key = S::Key::read(buf)?; ensure_zeros(buf, total - delete_op_size::())?; Ok(Operation::Delete(key)) } UPDATE_CONTEXT => { let payload = S::read_cfg(buf, cfg)?; ensure_zeros(buf, total - update_op_size::())?; Ok(Operation::Update(payload)) } COMMIT_CONTEXT => { let is_some = bool::read(buf)?; let metadata = if is_some { Some(V::read_cfg(buf, cfg)?) } else { ensure_zeros(buf, V::SIZE)?; None }; let floor_loc = Location::new(u64::read(buf)?); if !floor_loc.is_valid() { return Err(CodecError::Invalid( "storage::qmdb::any::operation::fixed::Operation", "commit floor location overflow", )); } ensure_zeros(buf, total - commit_op_size::())?; Ok(Operation::CommitFloor(metadata, floor_loc)) } e => Err(CodecError::InvalidEnum(e)), } } } // FixedSize for ordered fixed operations. impl FixedSize for Operation>> where F: Family, K: Array, V: FixedValue, update::Ordered>: FixedSize, { const SIZE: usize = total_op_size::>>(); } // FixedSize for unordered fixed operations. impl FixedSize for Operation>> where F: Family, K: Array, V: FixedValue, update::Unordered>: FixedSize, { const SIZE: usize = total_op_size::>>(); }