scylla_cql/deserialize/
frame_slice.rs

1//! Defines `FrameSlice`, a borrowed reference to a part of the frame.
2
3use bytes::Bytes;
4
5use crate::frame::frame_errors::LowLevelDeserializationError;
6use crate::frame::types;
7
8/// A borrowed reference to a part of the frame.
9//
10// # Design justification
11//
12// ## Why we need a borrowed type
13//
14// The reason why we need to store a borrowed slice is that we want a lifetime that is longer than one obtained
15// when coercing Bytes to a slice in the body of a function. That is, we want to allow deserializing types
16// that borrow from the frame, which resides in QueryResult.
17// Consider a function with the signature:
18//
19// fn fun(b: Bytes) { ... }
20//
21// This function cannot return a type that borrows from the frame, because any slice created from `b`
22// inside `fun` cannot escape `fun`.
23// Conversely, if a function has signature:
24//
25// fn fun(s: &'frame [u8]) { ... }
26//
27// then it can happily return types with lifetime 'frame.
28//
29// ## Why we need the full frame
30//
31// We don't. We only need to be able to return Bytes encompassing our subslice. However, the design choice
32// was made to only store a reference to the original Bytes object residing in QueryResult, so that we avoid
33// cloning Bytes when performing subslicing on FrameSlice. We delay the Bytes cloning, normally a moderately
34// expensive operation involving cloning an Arc, up until it is really needed.
35//
36// ## Why not different design
37//
38//      - why not a &'frame [u8] only? Because we want to enable deserializing types containing owned Bytes, too.
39//      - why not a Bytes only? Because we need to propagate the 'frame lifetime.
40//      - why not a &'frame Bytes only? Because we want to somehow represent subslices, and subslicing
41//        &'frame Bytes return Bytes, not &'frame Bytes.
42#[derive(Clone, Copy, Debug)]
43pub struct FrameSlice<'frame> {
44    // The actual subslice represented by this FrameSlice.
45    frame_subslice: &'frame [u8],
46
47    // Reference to the original Bytes object that this FrameSlice is derived
48    // from. It is used to convert the `mem` slice into a fully blown Bytes
49    // object via Bytes::slice_ref method.
50    original_frame: &'frame Bytes,
51}
52
53static EMPTY_BYTES: Bytes = Bytes::new();
54
55impl<'frame> FrameSlice<'frame> {
56    /// Creates a new FrameSlice from a reference of a Bytes object.
57    ///
58    /// This method is exposed to allow writing deserialization tests
59    /// for custom types.
60    #[inline]
61    pub fn new(frame: &'frame Bytes) -> Self {
62        Self {
63            frame_subslice: frame,
64            original_frame: frame,
65        }
66    }
67
68    /// Creates an empty FrameSlice.
69    #[inline]
70    pub fn new_empty() -> Self {
71        Self {
72            frame_subslice: &EMPTY_BYTES,
73            original_frame: &EMPTY_BYTES,
74        }
75    }
76
77    /// Creates a new FrameSlice from a reference to a slice.
78    ///
79    /// This method creates a not-fully-valid FrameSlice that does not hold
80    /// the valid original frame Bytes. Thus, it is intended to be used in
81    /// legacy code that does not operate on Bytes, but rather on borrowed slice only.
82    /// For correctness in an unlikely case that someone calls `to_bytes()` on such
83    /// a deficient slice, a special treatment is added there that copies
84    /// the slice into a new-allocation-based Bytes.
85    /// This is pub(crate) for the above reason.
86    #[inline]
87    pub(crate) fn new_borrowed(frame_subslice: &'frame [u8]) -> Self {
88        Self {
89            frame_subslice,
90            original_frame: &EMPTY_BYTES,
91        }
92    }
93
94    /// Returns `true` if the slice has length of 0.
95    #[inline]
96    pub fn is_empty(&self) -> bool {
97        self.frame_subslice.is_empty()
98    }
99
100    /// Returns the subslice.
101    #[inline]
102    pub fn as_slice(&self) -> &'frame [u8] {
103        self.frame_subslice
104    }
105
106    /// Returns a mutable reference to the subslice.
107    #[inline]
108    pub fn as_slice_mut(&mut self) -> &mut &'frame [u8] {
109        &mut self.frame_subslice
110    }
111
112    /// Returns a reference to the Bytes object which encompasses the whole frame slice.
113    ///
114    /// The Bytes object will usually be larger than the slice returned by
115    /// [FrameSlice::as_slice]. If you wish to obtain a new Bytes object that
116    /// points only to the subslice represented by the FrameSlice object,
117    /// see [FrameSlice::to_bytes].
118    #[inline]
119    pub fn as_original_frame_bytes(&self) -> &'frame Bytes {
120        self.original_frame
121    }
122
123    /// Returns a new Bytes object which is a subslice of the original Bytes
124    /// frame slice object.
125    #[inline]
126    // Check triggers because `self` is `Copy`, so `to_` should take it by value.
127    // TODO(2.0): Take self by value.
128    #[expect(clippy::wrong_self_convention)]
129    pub fn to_bytes(&self) -> Bytes {
130        if self.original_frame.is_empty() {
131            // For the borrowed, deficient version of FrameSlice - the one created with
132            // FrameSlice::new_borrowed to work properly in case someone calls
133            // FrameSlice::to_bytes on it (even though it's not intended for the borrowed version),
134            // the special case is introduced that creates new Bytes by copying the slice into
135            // a new allocation. Note that it's something unexpected to be ever done.
136            return Bytes::copy_from_slice(self.as_slice());
137        }
138
139        self.original_frame.slice_ref(self.frame_subslice)
140    }
141
142    /// Reads and consumes a `[bytes]` item from the beginning of the frame,
143    /// returning a subslice that encompasses that item.
144    ///
145    /// If the operation fails then the slice remains unchanged.
146    #[inline]
147    pub fn read_cql_bytes(
148        &mut self,
149    ) -> Result<Option<FrameSlice<'frame>>, LowLevelDeserializationError> {
150        // We copy the slice reference, not to mutate the FrameSlice in case of an error.
151        let mut slice = self.frame_subslice;
152
153        let cql_bytes = types::read_bytes_opt(&mut slice)?;
154
155        // `read_bytes_opt` hasn't failed, so now we must update the FrameSlice.
156        self.frame_subslice = slice;
157
158        Ok(cql_bytes.map(|slice| Self {
159            frame_subslice: slice,
160            original_frame: self.original_frame,
161        }))
162    }
163
164    /// Reads and consumes a fixed number of bytes from the beginning of the frame,
165    /// returning a subslice that encompasses them.
166    ///
167    /// If this slice is empty, returns `Ok(None)`.
168    /// Otherwise, if the slice does not contain enough data, it returns `Err`.
169    /// If the operation fails then the slice remains unchanged.
170    #[inline]
171    pub fn read_n_bytes(
172        &mut self,
173        count: usize,
174    ) -> Result<Option<FrameSlice<'frame>>, LowLevelDeserializationError> {
175        if self.is_empty() {
176            return Ok(None);
177        }
178
179        // We copy the slice reference, not to mutate the FrameSlice in case of an error.
180        let mut slice = self.frame_subslice;
181
182        let cql_bytes = types::read_raw_bytes(count, &mut slice)?;
183
184        // `read_raw_bytes` hasn't failed, so now we must update the FrameSlice.
185        self.frame_subslice = slice;
186
187        Ok(Some(Self {
188            frame_subslice: cql_bytes,
189            original_frame: self.original_frame,
190        }))
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use bytes::Bytes;
197
198    use super::super::tests::{serialize_cells, CELL1, CELL2};
199    use super::FrameSlice;
200
201    #[test]
202    fn test_cql_bytes_consumption() {
203        let frame = serialize_cells([Some(CELL1), None, Some(CELL2)]);
204        let mut slice = FrameSlice::new(&frame);
205        assert!(!slice.is_empty());
206
207        assert_eq!(
208            slice.read_cql_bytes().unwrap().map(|s| s.as_slice()),
209            Some(CELL1)
210        );
211        assert!(!slice.is_empty());
212        assert!(slice.read_cql_bytes().unwrap().is_none());
213        assert!(!slice.is_empty());
214        assert_eq!(
215            slice.read_cql_bytes().unwrap().map(|s| s.as_slice()),
216            Some(CELL2)
217        );
218        assert!(slice.is_empty());
219        slice.read_cql_bytes().unwrap_err();
220        assert!(slice.is_empty());
221    }
222
223    #[test]
224    fn test_cql_bytes_owned() {
225        let frame = serialize_cells([Some(CELL1), Some(CELL2)]);
226        let mut slice = FrameSlice::new(&frame);
227
228        let subslice1 = slice.read_cql_bytes().unwrap().unwrap();
229        let subslice2 = slice.read_cql_bytes().unwrap().unwrap();
230
231        assert_eq!(subslice1.as_slice(), CELL1);
232        assert_eq!(subslice2.as_slice(), CELL2);
233
234        assert_eq!(
235            subslice1.as_original_frame_bytes() as *const Bytes,
236            &frame as *const Bytes
237        );
238        assert_eq!(
239            subslice2.as_original_frame_bytes() as *const Bytes,
240            &frame as *const Bytes
241        );
242
243        let subslice1_bytes = subslice1.to_bytes();
244        let subslice2_bytes = subslice2.to_bytes();
245
246        assert_eq!(subslice1.as_slice(), subslice1_bytes.as_ref());
247        assert_eq!(subslice2.as_slice(), subslice2_bytes.as_ref());
248    }
249}