Skip to main content

sozu_command_lib/buffer/
growable.rs

1//! Growable ring buffer (`Buffer`).
2//!
3//! Same `position` / `end` / `capacity` discipline as `fixed.rs` but
4//! grows the backing `Vec<u8>` on demand up to a configured cap. Shift /
5//! insert / replace operations use overlapping-safe `std::ptr::copy`
6//! (NOT `copy_nonoverlapping`) with bounds checked against
7//! `self.capacity()`.
8//!
9//! The buffer carries command-channel frames, some of which hold protected
10//! values, so it keeps one invariant: every byte of `memory` outside the
11//! pending window `position..end` is zero. Consuming, reading, resetting,
12//! shifting, deleting or replacing erases the bytes that stop being pending,
13//! and growing or shrinking copies the pending window into a fresh zeroed
14//! allocation and erases the old one before releasing it, instead of letting
15//! `Vec::resize` / `Vec::shrink_to_fit` free it unwiped. `Drop` erases what is
16//! still pending, and `Debug` prints offsets only.
17
18use std::{
19    cmp, fmt,
20    io::{self, Read, Write},
21    mem, ptr,
22};
23
24use pool::Reset;
25use zeroize::Zeroize;
26
27#[derive(PartialEq, Eq, Clone)]
28pub struct Buffer {
29    memory: Vec<u8>,
30    capacity: usize,
31    position: usize,
32    end: usize,
33}
34
35impl fmt::Debug for Buffer {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.debug_struct("Buffer")
38            .field("capacity", &self.capacity)
39            .field("position", &self.position)
40            .field("end", &self.end)
41            .finish_non_exhaustive()
42    }
43}
44
45impl Drop for Buffer {
46    fn drop(&mut self) {
47        self.memory.zeroize();
48    }
49}
50
51impl Buffer {
52    pub fn with_capacity(capacity: usize) -> Buffer {
53        Buffer {
54            memory: vec![0; capacity],
55            capacity,
56            position: 0,
57            end: 0,
58        }
59    }
60
61    pub fn from_slice(sl: &[u8]) -> Buffer {
62        Buffer {
63            memory: Vec::from(sl),
64            capacity: sl.len(),
65            position: 0,
66            end: sl.len(),
67        }
68    }
69
70    pub fn grow(&mut self, new_size: usize) -> bool {
71        if self.capacity >= new_size {
72            return false;
73        }
74
75        self.reallocate(new_size);
76        self.capacity = new_size;
77        true
78    }
79
80    /// Shrink the buffer to target_size, preserving any pending data.
81    /// If pending data exceeds target_size, the buffer is not shrunk.
82    pub fn shrink(&mut self, target_size: usize) -> bool {
83        if target_size >= self.capacity {
84            return false;
85        }
86        self.shift();
87        if self.end > target_size {
88            return false;
89        }
90        self.reallocate(target_size);
91        self.capacity = target_size;
92        true
93    }
94
95    /// Move the pending window `position..end` to a fresh zeroed allocation of
96    /// `size` bytes, at the same offsets, and erase the old allocation before
97    /// it is released.
98    ///
99    /// `Vec::resize` and `Vec::shrink_to_fit` may free the old block with the
100    /// pending bytes still in it, and nothing can erase a block that is gone.
101    fn reallocate(&mut self, size: usize) {
102        debug_assert!(
103            self.end <= size,
104            "the pending window must fit the new allocation"
105        );
106        debug_assert!(
107            self.position <= self.end,
108            "the pending window must not be inverted"
109        );
110        let mut destination = vec![0; size];
111        destination[self.position..self.end].copy_from_slice(&self.memory[self.position..self.end]);
112        let mut previous = mem::replace(&mut self.memory, destination);
113        previous.zeroize();
114    }
115
116    /// Erase `memory[from..to]`, which has just stopped being pending.
117    fn erase(&mut self, from: usize, to: usize) {
118        debug_assert!(from <= to, "the erased range must not be inverted");
119        debug_assert!(
120            to <= self.memory.len(),
121            "the erased range must be in bounds"
122        );
123        self.memory[from..to].zeroize();
124    }
125
126    pub fn available_data(&self) -> usize {
127        self.end - self.position
128    }
129
130    pub fn available_space(&self) -> usize {
131        self.capacity - self.end
132    }
133
134    pub fn capacity(&self) -> usize {
135        self.capacity
136    }
137
138    pub fn empty(&self) -> bool {
139        self.position == self.end
140    }
141
142    pub fn consume(&mut self, count: usize) -> usize {
143        let cnt = cmp::min(count, self.available_data());
144        self.erase(self.position, self.position + cnt);
145        self.position += cnt;
146        if self.position > self.capacity / 2 {
147            //trace!("consume shift: pos {}, end {}", self.position, self.end);
148            self.shift();
149        }
150        cnt
151    }
152
153    /// displaces the end pointer to the right
154    pub fn fill(&mut self, count: usize) -> usize {
155        let cnt = cmp::min(count, self.available_space());
156        self.end += cnt;
157        if self.available_space() < self.available_data() + cnt {
158            //trace!("fill shift: pos {}, end {}", self.position, self.end);
159            self.shift();
160        }
161
162        cnt
163    }
164
165    pub fn reset(&mut self) {
166        self.erase(self.position, self.end);
167        self.position = 0;
168        self.end = 0;
169    }
170
171    pub fn data(&self) -> &[u8] {
172        &self.memory[self.position..self.end]
173    }
174
175    pub fn space(&mut self) -> &mut [u8] {
176        &mut self.memory[self.end..self.capacity]
177    }
178
179    pub fn shift(&mut self) {
180        if self.position > 0 {
181            let previous_end = self.end;
182            // SAFETY: src and dst point into `self.memory` (same
183            // allocation); the slice indexing above bounds-checks both
184            // ranges (`position..end` and `..length`) against the live
185            // buffer length. `ptr::copy` is overlap-safe.
186            unsafe {
187                let length = self.end - self.position;
188                ptr::copy(
189                    self.memory[self.position..self.end].as_ptr(),
190                    self.memory[..length].as_mut_ptr(),
191                    length,
192                );
193                self.position = 0;
194                self.end = length;
195            }
196            // The window moved down; what it used to cover above its new end
197            // is a stale copy.
198            self.erase(self.end, previous_end);
199        }
200    }
201
202    pub fn delete_slice(&mut self, start: usize, length: usize) -> Option<usize> {
203        if start + length >= self.available_data() {
204            return None;
205        }
206
207        let previous_end = self.end;
208        // SAFETY: src and dst point into `self.memory` (same allocation).
209        // The early-return above guarantees `start + length < available_data`,
210        // and slice indexing bounds-checks both `begin+length..end` and
211        // `begin..next_end` against the live buffer length. `ptr::copy` is
212        // overlap-safe.
213        unsafe {
214            let begin = self.position + start;
215            let next_end = self.end - length;
216            ptr::copy(
217                self.memory[begin + length..self.end].as_ptr(),
218                self.memory[begin..next_end].as_mut_ptr(),
219                self.end - (begin + length),
220            );
221            self.end = next_end;
222        }
223        // The tail moved down over the deleted bytes; its old position above
224        // the new end is a stale copy.
225        self.erase(self.end, previous_end);
226        Some(self.available_data())
227    }
228
229    pub fn replace_slice(&mut self, data: &[u8], start: usize, length: usize) -> Option<usize> {
230        let data_len = data.len();
231        if start + length > self.available_data()
232            || self.position + start + data_len > self.capacity
233        {
234            return None;
235        }
236
237        let previous_end = self.end;
238        // SAFETY: every `ptr::copy` below moves bytes inside `self.memory`
239        // (same allocation) or copies from the caller's `data` slice into
240        // it. The two early-return checks above bound the affected ranges
241        // against `available_data()` and `self.capacity`, and each slice
242        // indexing site is bounds-checked. `ptr::copy` is overlap-safe.
243        unsafe {
244            let begin = self.position + start;
245            let slice_end = begin + data_len;
246            // we reduced the data size
247            if data_len < length {
248                ptr::copy(
249                    data.as_ptr(),
250                    self.memory[begin..slice_end].as_mut_ptr(),
251                    data_len,
252                );
253
254                ptr::copy(
255                    self.memory[start + length..self.end].as_ptr(),
256                    self.memory[slice_end..].as_mut_ptr(),
257                    self.end - (start + length),
258                );
259                self.end -= length - data_len;
260
261            // we put more data in the buffer
262            } else {
263                ptr::copy(
264                    self.memory[start + length..self.end].as_ptr(),
265                    self.memory[start + data_len..].as_mut_ptr(),
266                    self.end - (start + length),
267                );
268                ptr::copy(
269                    data.as_ptr(),
270                    self.memory[begin..slice_end].as_mut_ptr(),
271                    data_len,
272                );
273                self.end += data_len - length;
274            }
275        }
276        // A reduced window leaves its old tail above the new end; a grown one
277        // has nothing to erase (`previous_end <= end`).
278        if self.end < previous_end {
279            self.erase(self.end, previous_end);
280        }
281        Some(self.available_data())
282    }
283
284    pub fn insert_slice(&mut self, data: &[u8], start: usize) -> Option<usize> {
285        let data_len = data.len();
286        if start > self.available_data() || self.position + self.end + data_len > self.capacity {
287            return None;
288        }
289
290        // SAFETY: both `ptr::copy` calls touch `self.memory` (same
291        // allocation) or copy from `data` into it. The early-return checks
292        // bound `start <= available_data` and the resulting tail
293        // `position + end + data_len <= capacity`, and each slice indexing
294        // site is bounds-checked. `ptr::copy` is overlap-safe.
295        unsafe {
296            let begin = self.position + start;
297            let slice_end = begin + data_len;
298            ptr::copy(
299                self.memory[start..self.end].as_ptr(),
300                self.memory[start + data_len..].as_mut_ptr(),
301                self.end - start,
302            );
303            ptr::copy(
304                data.as_ptr(),
305                self.memory[begin..slice_end].as_mut_ptr(),
306                data_len,
307            );
308            self.end += data_len;
309        }
310        Some(self.available_data())
311    }
312}
313
314impl Write for Buffer {
315    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
316        match self.space().write(buf) {
317            Ok(size) => {
318                self.fill(size);
319                Ok(size)
320            }
321            err => err,
322        }
323    }
324
325    fn flush(&mut self) -> io::Result<()> {
326        Ok(())
327    }
328}
329
330impl Read for Buffer {
331    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
332        let len = cmp::min(self.available_data(), buf.len());
333        // SAFETY: `len = min(available_data, buf.len())`, so the source
334        // range `position..position+len` lies inside `self.memory` and the
335        // destination `buf[..len]` fits the caller's `&mut [u8]`. The two
336        // slices are in different allocations; `ptr::copy` is overlap-safe
337        // regardless.
338        unsafe {
339            ptr::copy(
340                self.memory[self.position..self.position + len].as_ptr(),
341                buf.as_mut_ptr(),
342                len,
343            );
344        }
345        // The caller owns the copy now; the buffer keeps none of it.
346        self.erase(self.position, self.position + len);
347        self.position += len;
348        Ok(len)
349    }
350}
351
352impl Reset for Buffer {
353    fn reset(&mut self) {
354        self.reset();
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use std::io::Write;
361
362    use super::*;
363
364    #[test]
365    fn fill_and_consume() {
366        let mut b = Buffer::with_capacity(10);
367        assert_eq!(b.available_data(), 0);
368        assert_eq!(b.available_space(), 10);
369        let res = b.write(&b"abcd"[..]);
370        assert_eq!(res.ok(), Some(4));
371        assert_eq!(b.available_data(), 4);
372        assert_eq!(b.available_space(), 6);
373
374        assert_eq!(b.data(), &b"abcd"[..]);
375
376        b.consume(2);
377        assert_eq!(b.available_data(), 2);
378        assert_eq!(b.available_space(), 6);
379        assert_eq!(b.data(), &b"cd"[..]);
380
381        b.shift();
382        assert_eq!(b.available_data(), 2);
383        assert_eq!(b.available_space(), 8);
384        assert_eq!(b.data(), &b"cd"[..]);
385
386        assert_eq!(b.write(&b"efghijklmnop"[..]).ok(), Some(8));
387        assert_eq!(b.available_data(), 10);
388        assert_eq!(b.available_space(), 0);
389        assert_eq!(b.data(), &b"cdefghijkl"[..]);
390        b.shift();
391        assert_eq!(b.available_data(), 10);
392        assert_eq!(b.available_space(), 0);
393        assert_eq!(b.data(), &b"cdefghijkl"[..]);
394    }
395
396    #[test]
397    fn delete() {
398        let mut b = Buffer::with_capacity(10);
399        let _ = b.write(&b"abcdefgh"[..]).expect("should write");
400        assert_eq!(b.available_data(), 8);
401        assert_eq!(b.available_space(), 2);
402
403        assert_eq!(b.delete_slice(2, 3), Some(5));
404        assert_eq!(b.available_data(), 5);
405        assert_eq!(b.available_space(), 5);
406        assert_eq!(b.data(), &b"abfgh"[..]);
407
408        assert_eq!(b.delete_slice(5, 2), None);
409        assert_eq!(b.delete_slice(4, 2), None);
410    }
411
412    #[test]
413    fn replace() {
414        let mut b = Buffer::with_capacity(10);
415        let _ = b.write(&b"abcdefgh"[..]).expect("should write");
416        assert_eq!(b.available_data(), 8);
417        assert_eq!(b.available_space(), 2);
418
419        assert_eq!(b.replace_slice(&b"ABC"[..], 2, 3), Some(8));
420        assert_eq!(b.available_data(), 8);
421        assert_eq!(b.available_space(), 2);
422        assert_eq!(b.data(), &b"abABCfgh"[..]);
423
424        assert_eq!(b.replace_slice(&b"XYZ"[..], 8, 3), None);
425        assert_eq!(b.replace_slice(&b"XYZ"[..], 6, 3), None);
426
427        assert_eq!(b.replace_slice(&b"XYZ"[..], 2, 4), Some(7));
428        assert_eq!(b.available_data(), 7);
429        assert_eq!(b.available_space(), 3);
430        assert_eq!(b.data(), &b"abXYZgh"[..]);
431
432        assert_eq!(b.replace_slice(&b"123"[..], 2, 2), Some(8));
433        assert_eq!(b.available_data(), 8);
434        assert_eq!(b.available_space(), 2);
435        assert_eq!(b.data(), &b"ab123Zgh"[..]);
436    }
437}