scylla_cql/deserialize/
frame_slice.rs

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