use std::{num::NonZeroUsize, time::Duration};
use crate::{
app::memory_budgets::{BudgetBytes, MemoryBudgets},
codec::clamp_frame_length,
fragment::FragmentationConfig,
};
pub(super) const MIN_READ_TIMEOUT_MS: u64 = 1;
pub(super) const MAX_READ_TIMEOUT_MS: u64 = 86_400_000;
pub(super) const DEFAULT_READ_TIMEOUT_MS: u64 = 100;
const DEFAULT_FRAGMENT_TIMEOUT: Duration = Duration::from_secs(30);
const DEFAULT_MESSAGE_SIZE_MULTIPLIER: usize = 16;
const DEFAULT_MESSAGE_BUDGET_MULTIPLIER: usize = DEFAULT_MESSAGE_SIZE_MULTIPLIER;
const DEFAULT_CONNECTION_BUDGET_MULTIPLIER: usize = 64;
const DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER: usize = 64;
pub(super) fn default_fragmentation(frame_budget: usize) -> Option<FragmentationConfig> {
let frame_budget = clamp_frame_length(frame_budget);
let max_message =
NonZeroUsize::new(frame_budget.saturating_mul(DEFAULT_MESSAGE_SIZE_MULTIPLIER))
.or_else(|| NonZeroUsize::new(frame_budget));
max_message.and_then(|limit| {
FragmentationConfig::for_frame_budget(frame_budget, limit, DEFAULT_FRAGMENT_TIMEOUT)
})
}
fn derive_budget(frame_budget: usize, multiplier: usize) -> BudgetBytes {
let bytes =
NonZeroUsize::new(frame_budget.saturating_mul(multiplier)).unwrap_or(NonZeroUsize::MIN);
BudgetBytes::new(bytes)
}
#[must_use]
pub(super) fn default_memory_budgets(frame_budget: usize) -> MemoryBudgets {
let frame_budget = clamp_frame_length(frame_budget);
MemoryBudgets::new(
derive_budget(frame_budget, DEFAULT_MESSAGE_BUDGET_MULTIPLIER),
derive_budget(frame_budget, DEFAULT_CONNECTION_BUDGET_MULTIPLIER),
derive_budget(frame_budget, DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER),
)
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
use crate::codec::{MAX_FRAME_LENGTH, MIN_FRAME_LENGTH};
#[rstest]
#[case(1024)]
#[case(4096)]
fn default_budgets_scale_and_use_expected_multipliers(#[case] frame_budget: usize) {
let budgets = default_memory_budgets(frame_budget);
assert_eq!(
frame_budget * DEFAULT_MESSAGE_BUDGET_MULTIPLIER,
budgets.bytes_per_message().as_usize()
);
assert_eq!(
frame_budget * DEFAULT_CONNECTION_BUDGET_MULTIPLIER,
budgets.bytes_per_connection().as_usize()
);
assert_eq!(
frame_budget * DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER,
budgets.bytes_in_flight().as_usize()
);
}
#[test]
fn default_budgets_clamp_minimum_frame_budget() {
let budgets = default_memory_budgets(10);
assert_eq!(
MIN_FRAME_LENGTH * DEFAULT_MESSAGE_BUDGET_MULTIPLIER,
budgets.bytes_per_message().as_usize()
);
assert_eq!(
MIN_FRAME_LENGTH * DEFAULT_CONNECTION_BUDGET_MULTIPLIER,
budgets.bytes_per_connection().as_usize()
);
assert_eq!(
MIN_FRAME_LENGTH * DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER,
budgets.bytes_in_flight().as_usize()
);
}
#[test]
fn default_budgets_clamp_maximum_frame_budget() {
let budgets = default_memory_budgets(MAX_FRAME_LENGTH + 1);
assert_eq!(
MAX_FRAME_LENGTH * DEFAULT_MESSAGE_BUDGET_MULTIPLIER,
budgets.bytes_per_message().as_usize()
);
assert_eq!(
MAX_FRAME_LENGTH * DEFAULT_CONNECTION_BUDGET_MULTIPLIER,
budgets.bytes_per_connection().as_usize()
);
assert_eq!(
MAX_FRAME_LENGTH * DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER,
budgets.bytes_in_flight().as_usize()
);
}
#[test]
fn default_budgets_message_budget_aligns_with_fragmentation() {
let frame_budget = 2048_usize;
let budgets = default_memory_budgets(frame_budget);
let expected = frame_budget * DEFAULT_MESSAGE_SIZE_MULTIPLIER;
assert_eq!(
expected,
budgets.bytes_per_message().as_usize(),
"per-message budget does not match fragmentation multiplier"
);
}
}