Skip to main content

object_store/
payload.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use bytes::Bytes;
19use std::sync::Arc;
20
21/// A cheaply cloneable, ordered collection of [`Bytes`]
22///
23/// A [`PutPayload`] is fully materialized in memory. To upload large objects
24/// without buffering them entirely in memory, use the multipart API instead:
25/// [`ObjectStore::put_multipart_opts`], [`WriteMultipart`], or [`BufWriter`]
26///
27/// [`ObjectStore::put_multipart_opts`]: crate::ObjectStore::put_multipart_opts
28/// [`WriteMultipart`]: crate::WriteMultipart
29/// [`BufWriter`]: crate::buffered::BufWriter
30#[derive(Debug, Clone)]
31pub struct PutPayload(Arc<[Bytes]>);
32
33impl Default for PutPayload {
34    fn default() -> Self {
35        Self(Arc::new([]))
36    }
37}
38
39impl PutPayload {
40    /// Create a new empty [`PutPayload`]
41    pub fn new() -> Self {
42        Self::default()
43    }
44
45    /// Creates a [`PutPayload`] from a static slice
46    pub fn from_static(s: &'static [u8]) -> Self {
47        s.into()
48    }
49
50    /// Creates a [`PutPayload`] from a [`Bytes`]
51    pub fn from_bytes(s: Bytes) -> Self {
52        s.into()
53    }
54
55    /// Returns the total length of the [`Bytes`] in this payload
56    pub fn content_length(&self) -> usize {
57        self.0.iter().map(|b| b.len()).sum()
58    }
59
60    /// Returns an iterator over the [`Bytes`] in this payload
61    pub fn iter(&self) -> PutPayloadIter<'_> {
62        PutPayloadIter(self.0.iter())
63    }
64}
65
66impl AsRef<[Bytes]> for PutPayload {
67    fn as_ref(&self) -> &[Bytes] {
68        self.0.as_ref()
69    }
70}
71
72impl<'a> IntoIterator for &'a PutPayload {
73    type Item = &'a Bytes;
74    type IntoIter = PutPayloadIter<'a>;
75
76    fn into_iter(self) -> Self::IntoIter {
77        self.iter()
78    }
79}
80
81impl IntoIterator for PutPayload {
82    type Item = Bytes;
83    type IntoIter = PutPayloadIntoIter;
84
85    fn into_iter(self) -> Self::IntoIter {
86        PutPayloadIntoIter {
87            payload: self,
88            idx: 0,
89        }
90    }
91}
92
93/// An iterator over [`PutPayload`]
94#[derive(Debug)]
95pub struct PutPayloadIter<'a>(std::slice::Iter<'a, Bytes>);
96
97impl<'a> Iterator for PutPayloadIter<'a> {
98    type Item = &'a Bytes;
99
100    fn next(&mut self) -> Option<Self::Item> {
101        self.0.next()
102    }
103
104    fn size_hint(&self) -> (usize, Option<usize>) {
105        self.0.size_hint()
106    }
107}
108
109/// An owning iterator of [`PutPayload`]
110#[derive(Debug)]
111pub struct PutPayloadIntoIter {
112    payload: PutPayload,
113    idx: usize,
114}
115
116impl Iterator for PutPayloadIntoIter {
117    type Item = Bytes;
118
119    fn next(&mut self) -> Option<Self::Item> {
120        let p = self.payload.0.get(self.idx)?.clone();
121        self.idx += 1;
122        Some(p)
123    }
124
125    fn size_hint(&self) -> (usize, Option<usize>) {
126        let l = self.payload.0.len() - self.idx;
127        (l, Some(l))
128    }
129}
130
131impl From<Bytes> for PutPayload {
132    fn from(value: Bytes) -> Self {
133        Self(Arc::new([value]))
134    }
135}
136
137impl From<Vec<u8>> for PutPayload {
138    fn from(value: Vec<u8>) -> Self {
139        Self(Arc::new([value.into()]))
140    }
141}
142
143impl From<&'static str> for PutPayload {
144    fn from(value: &'static str) -> Self {
145        Bytes::from(value).into()
146    }
147}
148
149impl From<&'static [u8]> for PutPayload {
150    fn from(value: &'static [u8]) -> Self {
151        Bytes::from(value).into()
152    }
153}
154
155impl From<String> for PutPayload {
156    fn from(value: String) -> Self {
157        Bytes::from(value).into()
158    }
159}
160
161impl FromIterator<u8> for PutPayload {
162    fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
163        Bytes::from_iter(iter).into()
164    }
165}
166
167impl FromIterator<Bytes> for PutPayload {
168    fn from_iter<T: IntoIterator<Item = Bytes>>(iter: T) -> Self {
169        Self(iter.into_iter().collect())
170    }
171}
172
173impl From<PutPayload> for Bytes {
174    fn from(value: PutPayload) -> Self {
175        match value.0.len() {
176            0 => Self::new(),
177            1 => value.0[0].clone(),
178            _ => {
179                let mut buf = Vec::with_capacity(value.content_length());
180                value.iter().for_each(|x| buf.extend_from_slice(x));
181                buf.into()
182            }
183        }
184    }
185}
186
187/// A builder for [`PutPayload`] that avoids reallocating memory
188///
189/// Data is allocated in fixed blocks, which are flushed to [`Bytes`] once full.
190/// Unlike [`Vec`] this avoids needing to repeatedly reallocate blocks of memory,
191/// which typically involves copying all the previously written data to a new
192/// contiguous memory region.
193#[derive(Debug)]
194pub struct PutPayloadMut {
195    len: usize,
196    completed: Vec<Bytes>,
197    in_progress: Vec<u8>,
198    block_size: usize,
199}
200
201impl Default for PutPayloadMut {
202    fn default() -> Self {
203        Self {
204            len: 0,
205            completed: vec![],
206            in_progress: vec![],
207
208            block_size: 8 * 1024,
209        }
210    }
211}
212
213impl PutPayloadMut {
214    /// Create a new [`PutPayloadMut`]
215    pub fn new() -> Self {
216        Self::default()
217    }
218
219    /// Configures the minimum allocation size
220    ///
221    /// Defaults to 8KB
222    pub fn with_block_size(self, block_size: usize) -> Self {
223        Self { block_size, ..self }
224    }
225
226    /// Write bytes into this [`PutPayloadMut`]
227    ///
228    /// If there is an in-progress block, data will be first written to it, flushing
229    /// it to [`Bytes`] once full. If data remains to be written, a new block of memory
230    /// of at least the configured block size will be allocated, to hold the remaining data.
231    pub fn extend_from_slice(&mut self, slice: &[u8]) {
232        let remaining = self.in_progress.capacity() - self.in_progress.len();
233        let to_copy = remaining.min(slice.len());
234
235        self.in_progress.extend_from_slice(&slice[..to_copy]);
236        if self.in_progress.capacity() == self.in_progress.len() {
237            let new_cap = self.block_size.max(slice.len() - to_copy);
238            let completed = std::mem::replace(&mut self.in_progress, Vec::with_capacity(new_cap));
239            if !completed.is_empty() {
240                self.completed.push(completed.into())
241            }
242            self.in_progress.extend_from_slice(&slice[to_copy..])
243        }
244        self.len += slice.len();
245    }
246
247    /// Append a [`Bytes`] to this [`PutPayloadMut`] without copying
248    ///
249    /// This will close any currently buffered block populated by [`Self::extend_from_slice`],
250    /// and append `bytes` to this payload without copying.
251    pub fn push(&mut self, bytes: Bytes) {
252        if !self.in_progress.is_empty() {
253            let completed = std::mem::take(&mut self.in_progress);
254            self.completed.push(completed.into())
255        }
256        self.len += bytes.len();
257        self.completed.push(bytes);
258    }
259
260    /// Returns `true` if this [`PutPayloadMut`] contains no bytes
261    #[inline]
262    pub fn is_empty(&self) -> bool {
263        self.len == 0
264    }
265
266    /// Returns the total length of the [`Bytes`] in this payload
267    #[inline]
268    pub fn content_length(&self) -> usize {
269        self.len
270    }
271
272    /// Convert into [`PutPayload`]
273    pub fn freeze(mut self) -> PutPayload {
274        if !self.in_progress.is_empty() {
275            let completed = std::mem::take(&mut self.in_progress).into();
276            self.completed.push(completed);
277        }
278        PutPayload(self.completed.into())
279    }
280}
281
282impl From<PutPayloadMut> for PutPayload {
283    fn from(value: PutPayloadMut) -> Self {
284        value.freeze()
285    }
286}
287
288#[cfg(test)]
289mod test {
290    use crate::PutPayloadMut;
291
292    #[test]
293    fn test_put_payload() {
294        let mut chunk = PutPayloadMut::new().with_block_size(23);
295        chunk.extend_from_slice(&[1; 16]);
296        chunk.extend_from_slice(&[2; 32]);
297        chunk.extend_from_slice(&[2; 5]);
298        chunk.extend_from_slice(&[2; 21]);
299        chunk.extend_from_slice(&[2; 40]);
300        chunk.extend_from_slice(&[0; 0]);
301        chunk.push("foobar".into());
302
303        let payload = chunk.freeze();
304        assert_eq!(payload.content_length(), 120);
305
306        let chunks = payload.as_ref();
307        assert_eq!(chunks.len(), 6);
308
309        assert_eq!(chunks[0].len(), 23);
310        assert_eq!(chunks[1].len(), 25); // 32 - (23 - 16)
311        assert_eq!(chunks[2].len(), 23);
312        assert_eq!(chunks[3].len(), 23);
313        assert_eq!(chunks[4].len(), 20);
314        assert_eq!(chunks[5].len(), 6);
315    }
316
317    #[test]
318    fn test_content_length() {
319        let mut chunk = PutPayloadMut::new();
320        chunk.push(vec![0; 23].into());
321        assert_eq!(chunk.content_length(), 23);
322        chunk.extend_from_slice(&[0; 4]);
323        assert_eq!(chunk.content_length(), 27);
324        chunk.push(vec![0; 121].into());
325        assert_eq!(chunk.content_length(), 148);
326        let payload = chunk.freeze();
327        assert_eq!(payload.content_length(), 148);
328    }
329}