Skip to main content

tycho_storage_traits/
stored_value.rs

1use bytes::Buf;
2use smallvec::SmallVec;
3use tycho_types::cell::HashBytes;
4use tycho_types::models::{BlockId, BlockIdShort, ShardIdent};
5
6/// A trait for writing or reading data from a stack-allocated buffer
7pub trait StoredValue {
8    /// On-stack buffer size hint
9    const SIZE_HINT: usize;
10
11    /// On-stack buffer type (see [`smallvec::SmallVec`])
12    type OnStackSlice: smallvec::Array<Item = u8>;
13
14    /// Serializes the data to the buffer
15    fn serialize<T: StoredValueBuffer>(&self, buffer: &mut T);
16
17    /// Deserializes the data from the buffer.
18    ///
19    /// In case of successful deserialization it is guaranteed that `reader` will be
20    /// moved to the end of the deserialized data.
21    ///
22    /// NOTE: `reader` should not be used after this call in case of an error
23    fn deserialize(reader: &mut &[u8]) -> Self
24    where
25        Self: Sized;
26
27    /// Deserializes the data from the buffer.
28    ///
29    /// [`StoredValue::deserialize`]
30    #[inline(always)]
31    fn from_slice(mut data: &[u8]) -> Self
32    where
33        Self: Sized,
34    {
35        Self::deserialize(&mut data)
36    }
37
38    /// Constructs on-stack buffer with the serialized object
39    fn to_vec(&self) -> SmallVec<Self::OnStackSlice> {
40        let mut result = SmallVec::with_capacity(Self::SIZE_HINT);
41        self.serialize(&mut result);
42        result
43    }
44}
45
46/// A trait for simple buffer-based serialization
47pub trait StoredValueBuffer {
48    fn write_raw_slice(&mut self, data: &[u8]);
49}
50
51impl StoredValueBuffer for Vec<u8> {
52    #[inline(always)]
53    fn write_raw_slice(&mut self, data: &[u8]) {
54        self.extend_from_slice(data);
55    }
56}
57
58impl<T> StoredValueBuffer for SmallVec<T>
59where
60    T: smallvec::Array<Item = u8>,
61{
62    #[inline(always)]
63    fn write_raw_slice(&mut self, data: &[u8]) {
64        self.extend_from_slice(data);
65    }
66}
67
68impl StoredValue for BlockId {
69    /// 4 bytes workchain,
70    /// 8 bytes shard,
71    /// 4 bytes seqno,
72    /// 32 bytes root hash,
73    /// 32 bytes file hash
74    const SIZE_HINT: usize = ShardIdent::SIZE_HINT + 4 + 32 + 32;
75
76    type OnStackSlice = [u8; Self::SIZE_HINT];
77
78    fn serialize<T: StoredValueBuffer>(&self, buffer: &mut T) {
79        self.shard.serialize(buffer);
80        buffer.write_raw_slice(&self.seqno.to_be_bytes());
81        buffer.write_raw_slice(self.root_hash.as_slice());
82        buffer.write_raw_slice(self.file_hash.as_slice());
83    }
84
85    fn deserialize(reader: &mut &[u8]) -> Self
86    where
87        Self: Sized,
88    {
89        debug_assert!(reader.remaining() >= Self::SIZE_HINT);
90
91        let shard = ShardIdent::deserialize(reader);
92        let seqno = reader.get_u32();
93
94        let mut root_hash = HashBytes::default();
95        root_hash.0.copy_from_slice(&reader[..32]);
96        let mut file_hash = HashBytes::default();
97        file_hash.0.copy_from_slice(&reader[32..]);
98
99        Self {
100            shard,
101            seqno,
102            root_hash,
103            file_hash,
104        }
105    }
106}
107
108impl StoredValue for ShardIdent {
109    /// 4 bytes workchain
110    /// 8 bytes shard
111    const SIZE_HINT: usize = 4 + 8;
112
113    type OnStackSlice = [u8; Self::SIZE_HINT];
114
115    #[inline(always)]
116    fn serialize<T: StoredValueBuffer>(&self, buffer: &mut T) {
117        buffer.write_raw_slice(&self.workchain().to_be_bytes());
118        buffer.write_raw_slice(&self.prefix().to_be_bytes());
119    }
120
121    fn deserialize(reader: &mut &[u8]) -> Self
122    where
123        Self: Sized,
124    {
125        debug_assert!(reader.remaining() >= ShardIdent::SIZE_HINT);
126
127        let workchain = reader.get_u32() as i32;
128        let prefix = reader.get_u64();
129        unsafe { Self::new_unchecked(workchain, prefix) }
130    }
131}
132
133impl StoredValue for BlockIdShort {
134    /// 12 bytes shard ident
135    /// 4 bytes seqno
136    const SIZE_HINT: usize = ShardIdent::SIZE_HINT + 4;
137
138    type OnStackSlice = [u8; Self::SIZE_HINT];
139
140    #[inline(always)]
141    fn serialize<T: StoredValueBuffer>(&self, buffer: &mut T) {
142        self.shard.serialize(buffer);
143        buffer.write_raw_slice(&self.seqno.to_be_bytes());
144    }
145
146    fn deserialize(reader: &mut &[u8]) -> Self
147    where
148        Self: Sized,
149    {
150        debug_assert!(reader.remaining() >= BlockIdShort::SIZE_HINT);
151
152        let shard = ShardIdent::deserialize(reader);
153        let seqno = reader.get_u32();
154        Self { shard, seqno }
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn fully_on_stack() {
164        assert!(!BlockId::default().to_vec().spilled());
165        assert!(!BlockId::default().to_vec().spilled());
166    }
167}