Skip to main content

ab_core_primitives/pieces/
piece.rs

1#[cfg(all(test, feature = "serde"))]
2mod tests;
3
4use crate::pieces::cow_bytes::CowBytes;
5use crate::pieces::{InnerPiece, PieceHeader, Record};
6use ab_io_type::trivial_type::TrivialType;
7#[cfg(any(feature = "scale-codec", feature = "serde"))]
8use alloc::format;
9use alloc::vec::Vec;
10use bytes::{Bytes, BytesMut};
11use core::ops::{Deref, DerefMut};
12#[cfg(feature = "scale-codec")]
13use parity_scale_codec::{Decode, Encode, EncodeLike, Input, Output};
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Deserializer, Serialize, Serializer};
16
17/// A piece of archival history.
18///
19/// This version is allocated on the heap, for stack-allocated piece see [`InnerPiece`].
20///
21/// Internally piece contains a record and corresponding proof that together with segment
22/// root of the segment this piece belongs to can be used to verify that a piece belongs to
23/// the actual archival history of the blockchain.
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub struct Piece(pub(super) CowBytes);
26
27#[cfg(feature = "scale-codec")]
28impl Encode for Piece {
29    #[inline]
30    fn size_hint(&self) -> usize {
31        self.as_ref().size_hint()
32    }
33
34    #[inline]
35    fn encode_to<O>(&self, dest: &mut O)
36    where
37        O: Output + ?Sized,
38    {
39        self.as_ref().encode_to(dest);
40    }
41
42    #[inline]
43    fn encode(&self) -> Vec<u8> {
44        self.as_ref().encode()
45    }
46
47    #[inline]
48    fn using_encoded<R, F>(&self, f: F) -> R
49    where
50        F: FnOnce(&[u8]) -> R,
51    {
52        self.as_ref().using_encoded(f)
53    }
54}
55
56#[cfg(feature = "scale-codec")]
57impl EncodeLike for Piece {}
58
59#[cfg(feature = "scale-codec")]
60impl Decode for Piece {
61    fn decode<I>(input: &mut I) -> Result<Self, parity_scale_codec::Error>
62    where
63        I: Input,
64    {
65        let bytes =
66            Bytes::decode(input).map_err(|error| error.chain("Could not decode `Piece`"))?;
67
68        if bytes.len() != Self::SIZE {
69            return Err(
70                parity_scale_codec::Error::from("Incorrect Piece length").chain(format!(
71                    "Expected {} bytes, found {} bytes",
72                    Self::SIZE,
73                    bytes.len()
74                )),
75            );
76        }
77
78        Ok(Piece(CowBytes::Shared(bytes)))
79    }
80}
81
82#[cfg(feature = "serde")]
83impl Serialize for Piece {
84    #[inline]
85    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86    where
87        S: Serializer,
88    {
89        let bytes = match &self.0 {
90            CowBytes::Shared(bytes) => bytes.as_ref(),
91            CowBytes::Owned(bytes) => bytes.as_ref(),
92        };
93
94        if serializer.is_human_readable() {
95            hex::serde::serialize(bytes, serializer)
96        } else {
97            bytes.serialize(serializer)
98        }
99    }
100}
101
102#[cfg(feature = "serde")]
103impl<'de> Deserialize<'de> for Piece {
104    #[inline]
105    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
106    where
107        D: Deserializer<'de>,
108    {
109        let bytes = if deserializer.is_human_readable() {
110            Bytes::from(hex::serde::deserialize::<_, Vec<u8>>(deserializer)?)
111        } else {
112            Bytes::deserialize(deserializer)?
113        };
114        let length = bytes.len();
115
116        Piece::try_from(bytes).map_err(|()| {
117            serde::de::Error::invalid_length(
118                length,
119                &format!("Expected {} bytes", Piece::SIZE).as_str(),
120            )
121        })
122    }
123}
124
125impl Default for Piece {
126    #[inline]
127    fn default() -> Self {
128        Self(CowBytes::Owned(BytesMut::zeroed(Self::SIZE)))
129    }
130}
131
132impl From<Piece> for Vec<u8> {
133    #[inline]
134    fn from(piece: Piece) -> Self {
135        match piece.0 {
136            CowBytes::Shared(bytes) => bytes.to_vec(),
137            CowBytes::Owned(bytes) => Vec::from(bytes),
138        }
139    }
140}
141
142impl TryFrom<&[u8]> for Piece {
143    type Error = ();
144
145    #[inline]
146    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
147        if slice.len() != Self::SIZE {
148            return Err(());
149        }
150
151        Ok(Self(CowBytes::Shared(Bytes::copy_from_slice(slice))))
152    }
153}
154
155impl TryFrom<Vec<u8>> for Piece {
156    type Error = ();
157
158    #[inline]
159    fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
160        if vec.len() != Self::SIZE {
161            return Err(());
162        }
163
164        Ok(Self(CowBytes::Shared(Bytes::from(vec))))
165    }
166}
167
168impl TryFrom<Bytes> for Piece {
169    type Error = ();
170
171    #[inline]
172    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
173        if bytes.len() != Self::SIZE {
174            return Err(());
175        }
176
177        Ok(Self(CowBytes::Shared(bytes)))
178    }
179}
180
181impl TryFrom<BytesMut> for Piece {
182    type Error = ();
183
184    #[inline]
185    fn try_from(bytes: BytesMut) -> Result<Self, Self::Error> {
186        if bytes.len() != Self::SIZE {
187            return Err(());
188        }
189
190        Ok(Self(CowBytes::Owned(bytes)))
191    }
192}
193
194impl From<&InnerPiece> for Piece {
195    #[inline]
196    fn from(value: &InnerPiece) -> Self {
197        Self(CowBytes::Shared(Bytes::copy_from_slice(value.as_bytes())))
198    }
199}
200
201impl Deref for Piece {
202    type Target = InnerPiece;
203
204    #[inline]
205    fn deref(&self) -> &Self::Target {
206        // SAFETY: Piece bytes are always `InnerPiece::SIZE` long (checked in constructors),
207        // `InnerPiece` has alignment of 1 and any bit pattern is a valid value (both checked
208        // next to its definition)
209        unsafe { InnerPiece::from_bytes_unchecked(self.as_ref()) }
210    }
211}
212
213impl DerefMut for Piece {
214    #[inline]
215    fn deref_mut(&mut self) -> &mut Self::Target {
216        // SAFETY: Piece bytes are always `InnerPiece::SIZE` long (checked in constructors),
217        // `InnerPiece` has alignment of 1 and any bit pattern is a valid value (both checked
218        // next to its definition)
219        unsafe { InnerPiece::from_bytes_mut_unchecked(self.as_mut()) }
220    }
221}
222
223impl AsRef<[u8]> for Piece {
224    #[inline]
225    fn as_ref(&self) -> &[u8] {
226        self.0.as_ref()
227    }
228}
229
230impl AsMut<[u8]> for Piece {
231    #[inline]
232    fn as_mut(&mut self) -> &mut [u8] {
233        self.0.as_mut()
234    }
235}
236
237impl Piece {
238    /// Size of a piece (in bytes)
239    pub const SIZE: usize = InnerPiece::SIZE;
240
241    /// Create a piece from its header and record.
242    ///
243    /// Contents are written into a new allocation directly, without zeroing it first or going
244    /// through the stack.
245    pub fn from_parts(header: &PieceHeader, record: &Record) -> Self {
246        let mut bytes = BytesMut::with_capacity(Self::SIZE);
247        let inner_piece = bytes.spare_capacity_mut().as_mut_ptr().cast::<InnerPiece>();
248        // SAFETY: Capacity is sufficient for `InnerPiece`, whose alignment is `1`. Both fields are
249        // written, which initializes the whole `InnerPiece` since it has no padding.
250        unsafe {
251            (&raw mut (*inner_piece).header).write(*header);
252            (&raw mut (*inner_piece).record).copy_from_nonoverlapping(record, 1);
253            bytes.set_len(Self::SIZE);
254        }
255
256        Self(CowBytes::Shared(bytes.freeze()))
257    }
258
259    /// Ensure piece contains cheaply cloneable shared data.
260    ///
261    /// Internally piece uses CoW mechanism and can store either mutable owned data or data that is
262    /// cheap to clone, calling this method will ensure further clones will not result in additional
263    /// memory allocations.
264    pub fn to_shared(self) -> Self {
265        Self(match self.0 {
266            CowBytes::Shared(bytes) => CowBytes::Shared(bytes),
267            CowBytes::Owned(bytes) => CowBytes::Shared(bytes.freeze()),
268        })
269    }
270}