Skip to content

Commit

Permalink
feat(s2n-quic-core): add buffer::Deque implemention
Browse files Browse the repository at this point in the history
  • Loading branch information
camshaft committed May 10, 2024
1 parent bd37960 commit 6eb9040
Show file tree
Hide file tree
Showing 12 changed files with 1,225 additions and 7 deletions.
2 changes: 2 additions & 0 deletions quic/s2n-quic-core/src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

pub mod deque;
pub mod duplex;
mod error;
pub mod reader;
pub mod reassembler;
pub mod writer;

pub use deque::Deque;
pub use duplex::Duplex;
pub use error::Error;
pub use reader::Reader;
Expand Down
224 changes: 224 additions & 0 deletions quic/s2n-quic-core/src/buffer/deque.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::slice::deque;
use alloc::{boxed::Box, vec::Vec};
use core::{fmt, mem::MaybeUninit};

mod storage;

#[cfg(test)]
mod tests;

/// A fixed-capacity ring buffer for bytes
#[derive(Clone)]
pub struct Deque {
bytes: Box<[MaybeUninit<u8>]>,
head: usize,
len: usize,
}

impl fmt::Debug for Deque {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Deque")
.field("len", &self.len())
.field("capacity", &self.capacity())
.finish()
}
}

impl From<Vec<u8>> for Deque {
#[inline]
fn from(mut buffer: Vec<u8>) -> Deque {
let len = buffer.len();
let mut capacity = buffer.capacity();
if !capacity.is_power_of_two() {
capacity = capacity.next_power_of_two();
buffer.reserve_exact(capacity - len);
debug_assert!(capacity.is_power_of_two());
}

unsafe {
buffer.set_len(capacity);
}

let bytes = buffer.into_boxed_slice();
let ptr = Box::into_raw(bytes);
let bytes = unsafe { Box::from_raw(ptr as *mut [MaybeUninit<u8>]) };

Self {
bytes,
head: 0,
len,
}
}
}

impl Deque {
#[inline]
pub fn new(mut cap: usize) -> Self {
if !cap.is_power_of_two() {
cap = cap.next_power_of_two();
}

let mut bytes = Vec::<MaybeUninit<u8>>::with_capacity(cap);
unsafe {
bytes.set_len(cap);
}
let bytes = bytes.into_boxed_slice();

Self {
bytes,
head: 0,
len: 0,
}
}

#[inline]
pub fn capacity(&self) -> usize {
self.bytes.len()
}

#[inline]
pub fn remaining_capacity(&self) -> usize {
self.preconditions();
self.capacity() - self.len()
}

#[inline]
pub fn len(&self) -> usize {
self.len
}

#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}

/// Resets the filled bytes in the buffer
///
/// Note that data is not actually wiped with this method. If that behavior is desired then
/// calling [`Self::consume_filled`] should be preferred.
#[inline]
pub fn clear(&mut self) {
self.head = 0;
self.len = 0;
}

/// Consumes `len` bytes from the head of the buffer
///
/// # Panics
///
/// `len` MUST be less than or equal to [`Self::len`]
#[inline]
pub fn consume(&mut self, len: usize) {
self.preconditions();

debug_assert!(self.len() >= len);

if len >= self.len() {
self.clear();
return;
}

// Wrap the head around the capacity
self.head = deque::wrap(&self.bytes, self.head, len);
self.len -= len;
}

/// Returns the filled bytes in the buffer
#[inline]
pub fn filled(&mut self) -> deque::Pair<&mut [u8]> {
self.preconditions();

unsafe {
// SAFETY: cursors guarantee memory is filled
deque::filled(&mut self.bytes, self.head, self.len).assume_init_mut()
}
}

/// Returns and consumes `len` filled bytes in the buffer
///
/// # Panics
///
/// `len` MUST be less than or equal to [`Self::len`]
#[inline]
pub fn consume_filled(&mut self, len: usize) -> deque::Pair<&mut [u8]> {
self.preconditions();

let head = self.head;
let len = self.len.min(len);

self.consume(len);

unsafe {
// SAFETY: cursors guarantee memory is filled
deque::filled(&mut self.bytes, head, len).assume_init_mut()
}
}

/// Returns the unfilled bytes in the buffer
///
/// Callers will need to call [`Self::fill`] to indicate any writes that occurred to returned
/// slices.
#[inline]
pub fn unfilled(&mut self) -> deque::Pair<&mut [MaybeUninit<u8>]> {
self.preconditions();
deque::unfilled(&mut self.bytes, self.head, self.len)
}

/// Makes the buffer contiguous and contained in a single slice
#[inline]
pub fn make_contiguous(&mut self) -> &mut [u8] {
self.preconditions();
deque::make_contiguous(&mut self.bytes, &mut self.head, self.len);
let (head, tail) = self.filled().into();
debug_assert!(tail.is_empty());
head
}

/// Notifies the buffer that `len` bytes were written to it
///
/// # Safety
///
/// Callers must ensure the filled bytes were actually initialized
#[inline]
pub unsafe fn fill(&mut self, len: usize) -> Result<(), FillError> {
ensure!(self.remaining_capacity() >= len, Err(FillError(())));

self.len += len;

Ok(())
}

#[inline(always)]
fn preconditions(&self) {
unsafe {
assume!(deque::invariants(&self.bytes, self.head, self.len));
assume!(self.capacity().is_power_of_two());
}
}
}

#[derive(Clone, Copy, Debug, Default)]
pub struct FillError(());

impl fmt::Display for FillError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"the buffer does not have enough capacity for the provided fill amount"
)
}
}

#[cfg(feature = "std")]
impl std::error::Error for FillError {}

#[cfg(feature = "std")]
impl From<FillError> for std::io::Error {
#[inline]
fn from(value: FillError) -> Self {
Self::new(std::io::ErrorKind::InvalidInput, value)
}
}

0 comments on commit 6eb9040

Please sign in to comment.