use core::fmt;
use core::iter::FusedIterator;
use crate::qpack::field::section::decode::QpackFieldSectionBlocked;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackBlockedStream {
occupied: bool,
stream_id: u64,
required_insert_count: u64,
}
impl QpackBlockedStream {
pub const EMPTY: Self = Self {
occupied: false,
stream_id: 0,
required_insert_count: 0,
};
pub const fn stream_id(self) -> u64 {
self.stream_id
}
pub const fn required_insert_count(self) -> u64 {
self.required_insert_count
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackBlockedStreamsError {
MaximumBlockedStreamsExceedsStorage {
maximum: u64,
storage_capacity: usize,
},
BlockedStreamsLimitExceeded {
maximum: u64,
},
}
impl QpackBlockedStreamsError {
pub const fn is_decompression_failed(self) -> bool {
matches!(self, Self::BlockedStreamsLimitExceeded { .. })
}
pub const fn is_provisioning_error(self) -> bool {
matches!(self, Self::MaximumBlockedStreamsExceedsStorage { .. })
}
}
impl fmt::Display for QpackBlockedStreamsError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MaximumBlockedStreamsExceedsStorage {
maximum,
storage_capacity,
} => write!(
formatter,
"maximum blocked streams {maximum} exceeds storage capacity {storage_capacity}"
),
Self::BlockedStreamsLimitExceeded { maximum } => {
write!(formatter, "blocked streams exceed maximum {maximum}")
}
}
}
}
impl core::error::Error for QpackBlockedStreamsError {}
pub struct QpackBlockedStreams<'storage> {
records: &'storage mut [QpackBlockedStream],
maximum_blocked_streams: u64,
len: usize,
}
impl<'storage> QpackBlockedStreams<'storage> {
pub fn new(
records: &'storage mut [QpackBlockedStream],
maximum_blocked_streams: u64,
) -> Result<Self, QpackBlockedStreamsError> {
let maximum = match usize::try_from(maximum_blocked_streams) {
Ok(maximum) => maximum,
Err(_) => {
return Err(
QpackBlockedStreamsError::MaximumBlockedStreamsExceedsStorage {
maximum: maximum_blocked_streams,
storage_capacity: records.len(),
},
);
}
};
if maximum > records.len() {
return Err(
QpackBlockedStreamsError::MaximumBlockedStreamsExceedsStorage {
maximum: maximum_blocked_streams,
storage_capacity: records.len(),
},
);
}
for record in records.iter_mut() {
*record = QpackBlockedStream::EMPTY;
}
Ok(Self {
records,
maximum_blocked_streams,
len: 0,
})
}
pub const fn maximum_blocked_streams(&self) -> u64 {
self.maximum_blocked_streams
}
pub const fn storage_capacity(&self) -> usize {
self.records.len()
}
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub fn contains(&self, stream_id: u64) -> bool {
self.records
.iter()
.any(|record| record.occupied && record.stream_id == stream_id)
}
pub fn is_blocked(&self, stream_id: u64, insert_count: u64) -> bool {
self.records.iter().any(|record| {
record.occupied
&& record.stream_id == stream_id
&& record.required_insert_count > insert_count
})
}
pub fn register(
&mut self,
stream_id: u64,
blocked: QpackFieldSectionBlocked,
) -> Result<(), QpackBlockedStreamsError> {
for record in self.records.iter_mut() {
if record.occupied && record.stream_id == stream_id {
record.required_insert_count = record
.required_insert_count
.max(blocked.required_insert_count());
return Ok(());
}
}
if self.len >= self.maximum_blocked_streams as usize {
return Err(QpackBlockedStreamsError::BlockedStreamsLimitExceeded {
maximum: self.maximum_blocked_streams,
});
}
for record in self.records.iter_mut() {
if !record.occupied {
*record = QpackBlockedStream {
occupied: true,
stream_id,
required_insert_count: blocked.required_insert_count(),
};
self.len += 1;
return Ok(());
}
}
Err(QpackBlockedStreamsError::BlockedStreamsLimitExceeded {
maximum: self.maximum_blocked_streams,
})
}
pub fn remove(&mut self, stream_id: u64) -> bool {
for record in self.records.iter_mut() {
if record.occupied && record.stream_id == stream_id {
*record = QpackBlockedStream::EMPTY;
self.len -= 1;
return true;
}
}
false
}
pub fn ready(&self, insert_count: u64) -> QpackReadyBlockedStreamIter<'_> {
QpackReadyBlockedStreamIter {
remaining: self.records,
insert_count,
}
}
}
#[derive(Clone, Debug)]
pub struct QpackReadyBlockedStreamIter<'a> {
remaining: &'a [QpackBlockedStream],
insert_count: u64,
}
impl Iterator for QpackReadyBlockedStreamIter<'_> {
type Item = QpackBlockedStream;
fn next(&mut self) -> Option<Self::Item> {
while let Some((record, remaining)) = self.remaining.split_first() {
self.remaining = remaining;
if record.occupied && record.required_insert_count <= self.insert_count {
return Some(*record);
}
}
None
}
}
impl FusedIterator for QpackReadyBlockedStreamIter<'_> {}