molframe_core/io/input/limits.rs
1//! Hostile-input ceilings shared by readers and decoders.
2
3use crate::diagnostic::{Code, Diagnostic};
4
5const DEFAULT_DECOMPRESSED_BYTES: u64 = u64::MAX;
6const DEFAULT_COMPRESSION_RATIO: u64 = 1_000;
7const DEFAULT_NESTING_DEPTH: u32 = 64;
8
9/// Ceilings that turn hostile input into diagnostics rather than exhaustion.
10#[derive(Clone, Copy, PartialEq, Eq, Debug)]
11pub struct Limits {
12 /// Largest accepted size after decompression, in bytes.
13 ///
14 /// Unbounded by default. The ceiling exists to stop a small compressed
15 /// input expanding without limit, and that job is done by
16 /// [`Self::compression_ratio`], which bounds expansion against the bytes
17 /// actually supplied. An absolute ceiling additionally refuses large
18 /// *uncompressed* input, where there is nothing to defend against: the
19 /// file is already that size, and reading it cannot multiply it.
20 ///
21 /// A caller who wants an absolute ceiling sets one.
22 pub decompressed_bytes: u64,
23 /// Largest accepted ratio of decompressed to compressed size.
24 pub compression_ratio: u64,
25 /// Largest accepted declared row count for one category.
26 pub rows_per_category: u64,
27 /// Deepest accepted nesting.
28 pub nesting_depth: u32,
29 /// Largest accepted identifier dictionary.
30 pub dictionary_entries: u32,
31}
32
33impl Default for Limits {
34 fn default() -> Self {
35 Self {
36 decompressed_bytes: DEFAULT_DECOMPRESSED_BYTES,
37 compression_ratio: DEFAULT_COMPRESSION_RATIO,
38 rows_per_category: u64::MAX,
39 nesting_depth: DEFAULT_NESTING_DEPTH,
40 dictionary_entries: u32::MAX - 1,
41 }
42 }
43}
44
45impl Limits {
46 /// Builds the registered finding for a violated ceiling.
47 #[must_use]
48 pub fn exceeded(limit: &'static str, value: impl std::fmt::Display) -> Diagnostic {
49 Diagnostic::new(Code::E1901)
50 .with_context("limit", limit)
51 .with_context("value", value.to_string())
52 }
53}