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}