Skip to main content

qubit_io/buffered/
buffer.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9use std::collections::TryReserveError;
10
11use crate::util::try_reserve_vec;
12
13/// Low-level contiguous storage with a readable window and spare tail capacity.
14///
15/// `Buffer` stores initialized values and tracks a readable window as
16/// `data[position..limit]`. Values before `position` are considered consumed,
17/// and values after `limit` are spare capacity that callers may fill before
18/// advancing the limit.
19///
20/// The backing storage is fully initialized up front, so `T` is constrained to
21/// [`Clone`] + [`Default`]. Cloning is used when values enter or leave the
22/// buffer, while default initialization keeps every spare slot valid for the
23/// slice-based stream traits.
24///
25/// This type is intentionally a low-level, hot-path API. It exposes the full
26/// backing storage through [`Self::data`] and [`Self::data_mut`] so
27/// higher-level buffering code can avoid repeated slicing and bounds checks.
28/// Callers that mutate the backing storage directly must preserve the `position
29/// <= limit <= capacity` invariant and must only make initialized spare
30/// elements readable by calling [`Self::advance`].
31///
32/// The unsafe methods are for code that has already validated ranges at a
33/// higher level. They keep debug assertions for development builds, but those
34/// assertions are not a substitute for the documented safety preconditions.
35///
36/// # Window model
37///
38/// - [`Self::consumed`] — `data[..position]`, already-consumed elements.
39/// - [`Self::readable`] — `data[position..limit]`, readable elements.
40/// - [`Self::spare`] / [`Self::spare_mut`] — `data[limit..capacity]`, spare
41///   initialized storage.
42///
43/// # Examples
44///
45/// ```
46/// use qubit_io::Buffer;
47///
48/// let mut buffer = Buffer::<u8>::with_capacity(4);
49/// buffer.data_mut()[0..2].copy_from_slice(b"ab");
50/// // SAFETY: Two initialized spare elements fit in this buffer.
51/// unsafe {
52///     buffer.advance(2);
53/// }
54///
55/// assert_eq!(b"ab", buffer.readable());
56/// // SAFETY: One readable element is currently available.
57/// unsafe {
58///     buffer.consume(1);
59/// }
60/// assert_eq!(b"b", buffer.readable());
61/// ```
62///
63/// # Type Parameters
64///
65/// - `T`: Cloneable item type used for initialized backing storage.
66#[must_use]
67#[derive(Clone, Debug)]
68pub struct Buffer<T>
69where
70    T: Clone + Default,
71{
72    /// Fully initialized backing storage.
73    data: Vec<T>,
74    /// Start index of the readable window.
75    position: usize,
76    /// Exclusive end index of the readable window.
77    limit: usize,
78}
79
80impl<T> Buffer<T>
81where
82    T: Clone + Default,
83{
84    /// Creates an empty buffer with at least the requested capacity.
85    ///
86    /// A requested capacity of `0` is raised to `1`.
87    ///
88    /// # Parameters
89    ///
90    /// - `capacity`: Requested element capacity.
91    ///
92    /// # Returns
93    ///
94    /// Returns a buffer with `position == 0` and `limit == 0`.
95    ///
96    /// # Panics
97    ///
98    /// Panics if `T::default()` or `T::clone()` panics, or the requested
99    /// backing length exceeds [`Vec`]'s supported capacity.
100    #[inline]
101    pub fn with_capacity(capacity: usize) -> Self {
102        let capacity = capacity.max(1);
103        Self {
104            data: vec![T::default(); capacity],
105            position: 0,
106            limit: 0,
107        }
108    }
109
110    /// Tries to create an empty buffer with at least the requested capacity.
111    ///
112    /// A requested capacity of `0` is raised to `1`.
113    ///
114    /// # Parameters
115    ///
116    /// - `capacity`: Requested element capacity.
117    ///
118    /// # Returns
119    ///
120    /// Returns an empty buffer with at least one element of capacity.
121    ///
122    /// # Errors
123    ///
124    /// Returns the original allocation error when the backing storage cannot
125    /// reserve the requested capacity.
126    ///
127    /// # Panics
128    ///
129    /// Panics if `T::default()` or `T::clone()` panics.
130    #[inline]
131    pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
132        let capacity = capacity.max(1);
133        let mut data = Vec::new();
134        try_reserve_vec(&mut data, capacity)?;
135        if std::mem::size_of::<T>() == 0 {
136            data = vec![T::default(); capacity];
137        } else {
138            data.resize_with(capacity, T::default);
139        }
140        Ok(Self {
141            data,
142            position: 0,
143            limit: 0,
144        })
145    }
146
147    /// Tries to ensure that the total element capacity is at least `capacity`.
148    ///
149    /// Existing consumed, readable, and spare windows retain their positions.
150    ///
151    /// # Parameters
152    ///
153    /// - `capacity`: Required total element capacity.
154    ///
155    /// # Returns
156    ///
157    /// Returns `Ok(())` after the requested capacity is available.
158    ///
159    /// # Errors
160    ///
161    /// Returns the original allocation error when the backing storage cannot
162    /// reserve the additional capacity.
163    ///
164    /// # Panics
165    ///
166    /// Panics if growing the backing storage requires `T::default()` or
167    /// `T::clone()` and either operation panics.
168    #[inline]
169    pub fn try_reserve_capacity(&mut self, capacity: usize) -> Result<(), TryReserveError> {
170        if capacity <= self.data.len() {
171            return Ok(());
172        }
173        let additional = capacity - self.data.len();
174        try_reserve_vec(&mut self.data, additional)?;
175        if std::mem::size_of::<T>() == 0 {
176            let mut additional_data = vec![T::default(); additional];
177            self.data.append(&mut additional_data);
178        } else {
179            self.data.resize_with(capacity, T::default);
180        }
181        Ok(())
182    }
183
184    /// Returns the total element capacity.
185    ///
186    /// # Returns
187    ///
188    /// The length of the backing storage.
189    #[inline(always)]
190    #[must_use]
191    pub fn capacity(&self) -> usize {
192        self.data.len()
193    }
194
195    /// Returns the current readable cursor.
196    ///
197    /// # Returns
198    ///
199    /// The start index of the readable window.
200    #[inline(always)]
201    #[must_use]
202    pub const fn position(&self) -> usize {
203        self.position
204    }
205
206    /// Returns the current readable limit.
207    ///
208    /// # Returns
209    ///
210    /// The exclusive end index of the readable window.
211    #[inline(always)]
212    #[must_use]
213    pub const fn limit(&self) -> usize {
214        self.limit
215    }
216
217    /// Returns the backing storage.
218    ///
219    /// # Returns
220    ///
221    /// The full initialized backing slice.
222    #[inline(always)]
223    #[must_use]
224    pub fn data(&self) -> &[T] {
225        &self.data
226    }
227
228    /// Returns the mutable backing storage.
229    ///
230    /// Mutating elements outside the current readable or spare operation may
231    /// invalidate higher-level assumptions about buffered contents.
232    ///
233    /// # Returns
234    ///
235    /// The full initialized backing slice.
236    #[inline(always)]
237    #[must_use]
238    pub fn data_mut(&mut self) -> &mut [T] {
239        &mut self.data
240    }
241
242    /// Returns the number of readable elements.
243    ///
244    /// # Returns
245    ///
246    /// The length of `data[position..limit]`.
247    #[inline(always)]
248    #[must_use]
249    pub const fn available(&self) -> usize {
250        self.limit - self.position
251    }
252
253    /// Returns the consumed prefix.
254    ///
255    /// # Returns
256    ///
257    /// The slice `data[..position]`.
258    #[inline(always)]
259    #[must_use]
260    pub fn consumed(&self) -> &[T] {
261        &self.data[..self.position]
262    }
263
264    /// Returns the readable window.
265    ///
266    /// # Returns
267    ///
268    /// The slice `data[position..limit]`.
269    #[inline(always)]
270    #[must_use]
271    pub fn readable(&self) -> &[T] {
272        &self.data[self.position..self.limit]
273    }
274
275    /// Returns the spare tail.
276    ///
277    /// # Returns
278    ///
279    /// The slice `data[limit..capacity]`.
280    #[inline(always)]
281    #[must_use]
282    pub fn spare(&self) -> &[T] {
283        &self.data[self.limit..]
284    }
285
286    /// Returns the mutable spare tail.
287    ///
288    /// # Returns
289    ///
290    /// The slice `data[limit..capacity]`.
291    #[inline(always)]
292    #[must_use]
293    pub fn spare_mut(&mut self) -> &mut [T] {
294        let limit = self.limit;
295        &mut self.data[limit..]
296    }
297
298    /// Returns the number of spare elements after the limit.
299    ///
300    /// # Returns
301    ///
302    /// The length of `data[limit..]`.
303    #[inline(always)]
304    #[must_use]
305    pub fn spare_capacity(&self) -> usize {
306        self.data.len() - self.limit
307    }
308
309    /// Returns whether the readable window is empty.
310    ///
311    /// # Returns
312    ///
313    /// `true` when no elements are available for consumption.
314    #[inline(always)]
315    #[must_use]
316    pub const fn is_empty(&self) -> bool {
317        self.position == self.limit
318    }
319
320    /// Returns whether the spare tail is empty.
321    ///
322    /// # Returns
323    ///
324    /// `true` when `limit == capacity`.
325    #[inline(always)]
326    #[must_use]
327    pub fn is_full(&self) -> bool {
328        self.limit == self.data.len()
329    }
330
331    /// Returns raw spare-tail parts for hot-path callers.
332    ///
333    /// The returned slice is the full backing storage. `index` is the start of
334    /// the spare window, and `count` is the number of spare elements. Callers
335    /// that need a slice can use [`Self::spare_mut`]; callers that already
336    /// validated bounds can pass `buffer` and `index` directly to indexed
337    /// unchecked operations that write from `index`.
338    ///
339    /// # Returns
340    ///
341    /// The backing storage, the spare start index, and the spare element count.
342    #[inline(always)]
343    #[must_use]
344    pub fn spare_raw_parts_mut(&mut self) -> (&mut [T], usize, usize) {
345        let index = self.limit;
346        let count = self.spare_capacity();
347        (self.data_mut(), index, count)
348    }
349
350    /// Clears all buffered contents.
351    ///
352    /// This resets both cursors to zero without modifying stored values.
353    #[inline(always)]
354    pub fn clear(&mut self) {
355        self.position = 0;
356        self.limit = 0;
357    }
358
359    /// Advances the readable cursor without checking bounds.
360    ///
361    /// # Parameters
362    ///
363    /// - `count`: Number of readable elements to consume.
364    ///
365    /// # Panics
366    ///
367    /// Panics in debug builds if `count > self.available()`.
368    ///
369    /// # Safety
370    ///
371    /// The caller must guarantee that `count <= self.available()`.
372    #[inline(always)]
373    pub unsafe fn consume(&mut self, count: usize) {
374        debug_assert!(count <= self.available(), "unchecked consume exceeds available buffer");
375        self.position += count;
376    }
377
378    /// Advances the readable limit without checking bounds.
379    ///
380    /// # Parameters
381    ///
382    /// - `count`: Number of initialized spare elements to make readable.
383    ///
384    /// # Panics
385    ///
386    /// Panics in debug builds if `count > self.spare_capacity()`.
387    ///
388    /// # Safety
389    ///
390    /// The caller must guarantee that `count <= self.spare_capacity()`.
391    #[inline(always)]
392    pub unsafe fn advance(&mut self, count: usize) {
393        debug_assert!(
394            count <= self.spare_capacity(),
395            "unchecked advance exceeds spare buffer capacity"
396        );
397        self.limit += count;
398    }
399
400    /// Moves unread elements to the front of the backing storage.
401    ///
402    /// Consumed elements are discarded. The unread element count is preserved,
403    /// and the readable window starts at zero after compaction.
404    #[inline]
405    pub fn compact(&mut self) {
406        let available = self.available();
407        if available == 0 {
408            self.clear();
409            return;
410        }
411        if self.position != 0 {
412            self.data[..self.limit].rotate_left(self.position);
413        }
414        self.position = 0;
415        self.limit = available;
416    }
417
418    /// Copies values from an external slice into the spare tail.
419    ///
420    /// The cloned values are made readable by advancing the limit by `count`.
421    ///
422    /// # Parameters
423    ///
424    /// - `input`: Source storage.
425    /// - `input_index`: Start index inside `input`.
426    /// - `count`: Number of values to copy.
427    ///
428    /// # Panics
429    ///
430    /// Panics if cloning an input item panics. Debug builds also panic if the
431    /// requested input range does not fit or `count > self.spare_capacity()`.
432    ///
433    /// # Safety
434    ///
435    /// The caller must guarantee that `input_index..input_index + count` is a
436    /// valid range inside `input`, that the addition does not overflow, that
437    /// `count <= self.spare_capacity()`, and that the source range does not
438    /// overlap with this buffer's destination range.
439    #[inline]
440    pub unsafe fn copy_from(&mut self, input: &[T], input_index: usize, count: usize) {
441        debug_assert!(
442            input_index <= input.len() && count <= input.len() - input_index,
443            "unchecked source range exceeds input buffer"
444        );
445        debug_assert!(
446            count <= self.spare_capacity(),
447            "unchecked copy exceeds spare buffer capacity"
448        );
449        unsafe {
450            let input = input.get_unchecked(input_index..input_index + count);
451            let limit = self.limit;
452            let destination = self.data.get_unchecked_mut(limit..limit + count);
453            destination.clone_from_slice(input);
454            // SAFETY: The caller guarantees that the cloned range fits the
455            // spare window, and the limit advances only after cloning succeeds.
456            self.advance(count);
457        }
458    }
459
460    /// Copies readable values into an external slice.
461    ///
462    /// The cloned values are consumed by advancing the position by `count`.
463    ///
464    /// # Parameters
465    ///
466    /// - `output`: Destination storage.
467    /// - `output_index`: Start index inside `output`.
468    /// - `count`: Number of values to copy.
469    ///
470    /// # Panics
471    ///
472    /// Panics if cloning a readable item panics. Debug builds also panic if the
473    /// requested output range does not fit or `count > self.available()`.
474    ///
475    /// # Safety
476    ///
477    /// The caller must guarantee that `output_index..output_index + count` is
478    /// a valid range inside `output`, that the addition does not overflow, that
479    /// `count <= self.available()`, and that the source range does not overlap
480    /// with the destination range.
481    #[inline]
482    pub unsafe fn copy_to(&mut self, output: &mut [T], output_index: usize, count: usize) {
483        debug_assert!(
484            output_index <= output.len() && count <= output.len() - output_index,
485            "unchecked destination range exceeds output buffer"
486        );
487        debug_assert!(
488            count <= self.available(),
489            "unchecked copy exceeds available buffer items"
490        );
491        unsafe {
492            let position = self.position;
493            let source = self.data.get_unchecked(position..position + count);
494            let output = output.get_unchecked_mut(output_index..output_index + count);
495            output.clone_from_slice(source);
496            // SAFETY: The caller guarantees that the cloned range fits the
497            // readable window, and the position advances only after cloning
498            // succeeds.
499            self.consume(count);
500        }
501    }
502
503    /// Moves the readable cursor backward without checking bounds.
504    ///
505    /// # Parameters
506    ///
507    /// - `count`: Number of already-consumed elements to make readable again.
508    ///
509    /// # Panics
510    ///
511    /// Panics in debug builds if `count > self.position()`.
512    ///
513    /// # Safety
514    ///
515    /// The caller must guarantee that `count <= self.position()`.
516    #[inline(always)]
517    pub(crate) unsafe fn rewind(&mut self, count: usize) {
518        debug_assert!(
519            count <= self.position,
520            "unchecked rewind exceeds consumed buffer prefix"
521        );
522        self.position -= count;
523    }
524}