pub const MAX_STREAM_BYTES: u64 = 1_125_899_906_842_624;
pub const MAX_STREAM_CHUNK_BYTES: usize = 16_777_216;
pub const MAX_STREAM_CHUNKS: u32 = 16_777_216;
pub const MAX_STREAM_OBSERVATIONS: u32 = 67_108_864;
pub const MAX_CONSECUTIVE_ZERO_PROGRESS: u16 = 4_096;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum StreamKind {
FiniteUpload,
FiniteDownload,
CallerCancelledEvent,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum StreamFraming {
Declared(u64),
ExecutorOwned,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum StreamSinkMode {
Transactional,
Direct,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum StreamLimitsError {
ByteLimitZero,
ByteLimitTooLarge,
ChunkBytesZero,
ChunkBytesTooLarge,
ChunkLimitZero,
ChunkLimitTooLarge,
ObservationLimitTooSmall,
ObservationLimitTooLarge,
ZeroProgressLimitTooLarge,
}
impl_static_error!(StreamLimitsError,
Self::ByteLimitZero => "stream byte limit is zero",
Self::ByteLimitTooLarge => "stream byte limit exceeds the global ceiling",
Self::ChunkBytesZero => "stream chunk-size limit is zero",
Self::ChunkBytesTooLarge => "stream chunk-size limit exceeds the global ceiling",
Self::ChunkLimitZero => "stream chunk limit is zero",
Self::ChunkLimitTooLarge => "stream chunk limit exceeds the global ceiling",
Self::ObservationLimitTooSmall => "stream observation limit cannot admit every chunk",
Self::ObservationLimitTooLarge => "stream observation limit exceeds the global ceiling",
Self::ZeroProgressLimitTooLarge => "stream zero-progress limit is too large",
);
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct StreamLimits {
max_bytes: u64,
max_chunk_bytes: usize,
max_chunks: u32,
max_observations: u32,
max_consecutive_zero_progress: u16,
}
impl StreamLimits {
pub const fn new(
max_bytes: u64,
max_chunk_bytes: usize,
max_chunks: u32,
max_observations: u32,
max_consecutive_zero_progress: u16,
) -> Result<Self, StreamLimitsError> {
if max_bytes == 0 {
return Err(StreamLimitsError::ByteLimitZero);
}
if max_bytes > MAX_STREAM_BYTES {
return Err(StreamLimitsError::ByteLimitTooLarge);
}
if max_chunk_bytes == 0 {
return Err(StreamLimitsError::ChunkBytesZero);
}
if max_chunk_bytes > MAX_STREAM_CHUNK_BYTES {
return Err(StreamLimitsError::ChunkBytesTooLarge);
}
if max_chunks == 0 {
return Err(StreamLimitsError::ChunkLimitZero);
}
if max_chunks > MAX_STREAM_CHUNKS {
return Err(StreamLimitsError::ChunkLimitTooLarge);
}
if max_observations < max_chunks {
return Err(StreamLimitsError::ObservationLimitTooSmall);
}
if max_observations > MAX_STREAM_OBSERVATIONS {
return Err(StreamLimitsError::ObservationLimitTooLarge);
}
if max_consecutive_zero_progress > MAX_CONSECUTIVE_ZERO_PROGRESS
|| max_consecutive_zero_progress as u32 > max_observations
{
return Err(StreamLimitsError::ZeroProgressLimitTooLarge);
}
Ok(Self {
max_bytes,
max_chunk_bytes,
max_chunks,
max_observations,
max_consecutive_zero_progress,
})
}
#[must_use]
pub const fn max_bytes(self) -> u64 {
self.max_bytes
}
#[must_use]
pub const fn max_chunk_bytes(self) -> usize {
self.max_chunk_bytes
}
#[must_use]
pub const fn max_chunks(self) -> u32 {
self.max_chunks
}
#[must_use]
pub const fn max_observations(self) -> u32 {
self.max_observations
}
#[must_use]
pub const fn max_consecutive_zero_progress(self) -> u16 {
self.max_consecutive_zero_progress
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum StreamPolicyError {
DeclaredLengthTooLarge,
EventRequiresExecutorFraming,
}
impl_static_error!(StreamPolicyError,
Self::DeclaredLengthTooLarge => "declared stream length exceeds the operation limit",
Self::EventRequiresExecutorFraming => "event stream requires executor-owned framing",
);
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct StreamPolicy {
kind: StreamKind,
framing: StreamFraming,
sink_mode: StreamSinkMode,
limits: StreamLimits,
}
impl StreamPolicy {
pub const fn new(
kind: StreamKind,
framing: StreamFraming,
sink_mode: StreamSinkMode,
limits: StreamLimits,
) -> Result<Self, StreamPolicyError> {
if let StreamFraming::Declared(length) = framing
&& length > limits.max_bytes
{
return Err(StreamPolicyError::DeclaredLengthTooLarge);
}
if matches!(kind, StreamKind::CallerCancelledEvent)
&& !matches!(framing, StreamFraming::ExecutorOwned)
{
return Err(StreamPolicyError::EventRequiresExecutorFraming);
}
Ok(Self {
kind,
framing,
sink_mode,
limits,
})
}
#[must_use]
pub const fn kind(self) -> StreamKind {
self.kind
}
#[must_use]
pub const fn framing(self) -> StreamFraming {
self.framing
}
#[must_use]
pub const fn sink_mode(self) -> StreamSinkMode {
self.sink_mode
}
#[must_use]
pub const fn limits(self) -> StreamLimits {
self.limits
}
}
pub(super) const fn partial_state(
mode: StreamSinkMode,
write_attempted: bool,
) -> super::StreamPartialState {
if !write_attempted {
super::StreamPartialState::Clean
} else {
match mode {
StreamSinkMode::Transactional => super::StreamPartialState::RollbackRequired,
StreamSinkMode::Direct => super::StreamPartialState::Dirty,
}
}
}