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}