#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Limits {
max_depth: usize,
alias_factor: usize,
alias_floor: usize,
alias_enabled: bool,
alias_bytes_factor: usize,
alias_bytes_floor: usize,
alias_bytes_enabled: bool,
max_document_size: usize,
max_scalar_len: usize,
max_documents: usize,
max_typed_depth: usize,
max_diagnostics: usize,
}
const MIB: usize = 1024 * 1024;
impl Limits {
#[must_use]
pub const fn new() -> Self {
Self {
max_depth: 1024,
alias_factor: 10,
alias_floor: 1_000_000,
alias_enabled: true,
alias_bytes_factor: 10,
alias_bytes_floor: 64 * MIB,
alias_bytes_enabled: true,
max_document_size: 256 * MIB,
max_scalar_len: 64 * MIB,
max_documents: usize::MAX,
max_typed_depth: 128,
max_diagnostics: 100_000,
}
}
#[must_use]
pub const fn untrusted() -> Self {
Self {
max_depth: 128,
alias_factor: 2,
alias_floor: 100_000,
alias_enabled: true,
alias_bytes_factor: 2,
alias_bytes_floor: 8 * MIB,
alias_bytes_enabled: true,
max_document_size: 16 * MIB,
max_scalar_len: MIB,
max_documents: 1000,
max_typed_depth: 64,
max_diagnostics: 1000,
}
}
#[must_use]
pub const fn unlimited() -> Self {
Self {
max_depth: usize::MAX,
alias_factor: 0,
alias_floor: usize::MAX,
alias_enabled: false,
alias_bytes_factor: 0,
alias_bytes_floor: usize::MAX,
alias_bytes_enabled: false,
max_document_size: usize::MAX,
max_scalar_len: usize::MAX,
max_documents: usize::MAX,
max_typed_depth: usize::MAX,
max_diagnostics: usize::MAX,
}
}
#[must_use]
pub const fn max_depth(mut self, depth: usize) -> Self {
self.max_depth = depth;
self
}
#[must_use]
pub const fn alias_budget(mut self, nodes: usize) -> Self {
self.alias_factor = 0;
self.alias_floor = nodes;
self.alias_enabled = true;
self
}
#[must_use]
pub const fn alias_budget_ratio(mut self, factor: usize, floor: usize) -> Self {
self.alias_factor = factor;
self.alias_floor = floor;
self.alias_enabled = true;
self
}
#[must_use]
pub const fn alias_byte_budget(mut self, bytes: usize) -> Self {
self.alias_bytes_factor = 0;
self.alias_bytes_floor = bytes;
self.alias_bytes_enabled = true;
self
}
#[must_use]
pub const fn alias_byte_budget_ratio(mut self, factor: usize, floor: usize) -> Self {
self.alias_bytes_factor = factor;
self.alias_bytes_floor = floor;
self.alias_bytes_enabled = true;
self
}
#[must_use]
pub const fn max_document_size(mut self, bytes: usize) -> Self {
self.max_document_size = bytes;
self
}
#[must_use]
pub const fn max_scalar_len(mut self, bytes: usize) -> Self {
self.max_scalar_len = bytes;
self
}
#[must_use]
pub const fn max_documents(mut self, documents: usize) -> Self {
self.max_documents = documents;
self
}
#[must_use]
pub const fn max_typed_depth(mut self, depth: usize) -> Self {
self.max_typed_depth = depth;
self
}
#[must_use]
pub const fn max_diagnostics(mut self, diagnostics: usize) -> Self {
self.max_diagnostics = diagnostics;
self
}
#[must_use]
pub const fn get_max_diagnostics(&self) -> usize {
self.max_diagnostics
}
#[must_use]
pub const fn get_max_typed_depth(&self) -> usize {
self.max_typed_depth
}
#[must_use]
pub const fn get_max_depth(&self) -> usize {
self.max_depth
}
#[must_use]
pub const fn get_alias_budget(&self, own_nodes: usize) -> Option<usize> {
if !self.alias_enabled {
return None;
}
let scaled = self.alias_factor.saturating_mul(own_nodes);
Some(if scaled > self.alias_floor {
scaled
} else {
self.alias_floor
})
}
#[must_use]
pub const fn get_alias_byte_budget(&self, input_bytes: usize) -> Option<usize> {
if !self.alias_bytes_enabled {
return None;
}
let scaled = self.alias_bytes_factor.saturating_mul(input_bytes);
Some(if scaled > self.alias_bytes_floor {
scaled
} else {
self.alias_bytes_floor
})
}
#[must_use]
pub const fn get_max_document_size(&self) -> usize {
self.max_document_size
}
#[must_use]
pub const fn get_max_scalar_len(&self) -> usize {
self.max_scalar_len
}
#[must_use]
pub const fn get_max_documents(&self) -> usize {
self.max_documents
}
}
impl Default for Limits {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn profiles_have_the_q4_5_values() {
let d = Limits::default();
assert_eq!(d, Limits::new());
assert_eq!(d.get_max_depth(), 1024);
assert_eq!(d.get_alias_budget(10), Some(1_000_000));
assert_eq!(d.get_alias_budget(200_000), Some(2_000_000));
assert_eq!(d.get_max_document_size(), 256 * 1024 * 1024);
assert_eq!(d.get_max_scalar_len(), 64 * 1024 * 1024);
assert_eq!(d.get_max_documents(), usize::MAX);
assert_eq!(d.get_max_typed_depth(), 128);
assert_eq!(d.get_max_diagnostics(), 100_000);
assert_eq!(d.get_alias_byte_budget(0), Some(64 * 1024 * 1024));
assert_eq!(
d.get_alias_byte_budget(10 * 1024 * 1024),
Some(100 * 1024 * 1024)
);
let u = Limits::untrusted();
assert_eq!(u.get_max_depth(), 128);
assert_eq!(u.get_alias_budget(10), Some(100_000));
assert_eq!(u.get_alias_budget(60_000), Some(120_000));
assert_eq!(u.get_max_document_size(), 16 * 1024 * 1024);
assert_eq!(u.get_max_scalar_len(), 1024 * 1024);
assert_eq!(u.get_max_documents(), 1000);
assert_eq!(u.get_max_typed_depth(), 64);
assert_eq!(u.get_max_diagnostics(), 1000);
assert_eq!(u.get_alias_byte_budget(1000), Some(8 * 1024 * 1024));
assert_eq!(
u.get_alias_byte_budget(5 * 1024 * 1024),
Some(10 * 1024 * 1024)
);
let n = Limits::unlimited();
assert_eq!(n.get_max_depth(), usize::MAX);
assert_eq!(n.get_alias_budget(usize::MAX), None);
assert_eq!(n.get_max_document_size(), usize::MAX);
assert_eq!(n.get_max_scalar_len(), usize::MAX);
assert_eq!(n.get_max_documents(), usize::MAX);
assert_eq!(n.get_max_typed_depth(), usize::MAX);
assert_eq!(n.get_max_diagnostics(), usize::MAX);
assert_eq!(n.get_alias_byte_budget(usize::MAX), None);
}
#[test]
fn setters_chain_and_are_const() {
const L: Limits = Limits::untrusted()
.max_depth(7)
.max_document_size(8)
.max_scalar_len(9)
.max_documents(10)
.alias_budget(11)
.max_typed_depth(12)
.max_diagnostics(13);
assert_eq!(L.get_max_typed_depth(), 12);
assert_eq!(L.get_max_diagnostics(), 13);
assert_eq!(
(
L.get_max_depth(),
L.get_max_document_size(),
L.get_max_scalar_len(),
L.get_max_documents(),
L.get_alias_budget(1_000),
),
(7, 8, 9, 10, Some(11))
);
let r = Limits::unlimited().alias_budget_ratio(3, 5);
assert_eq!(r.get_alias_budget(1), Some(5));
assert_eq!(r.get_alias_budget(4), Some(12));
assert_eq!(r.get_alias_budget(usize::MAX), Some(usize::MAX));
assert_ne!(r, Limits::unlimited());
const B: Limits = Limits::unlimited().alias_byte_budget(7);
assert_eq!(B.get_alias_byte_budget(1_000_000), Some(7));
assert_eq!(B.get_alias_budget(1), None);
let b = Limits::unlimited().alias_byte_budget_ratio(3, 5);
assert_eq!(b.get_alias_byte_budget(1), Some(5));
assert_eq!(b.get_alias_byte_budget(4), Some(12));
assert_eq!(b.get_alias_byte_budget(usize::MAX), Some(usize::MAX));
assert_eq!(b.get_alias_budget(1), None);
let n = Limits::default().alias_budget(3);
assert_eq!(n.get_alias_byte_budget(0), Some(64 * 1024 * 1024));
}
}