pub struct Limits { /* private fields */ }Expand description
Resource limits: secondary safeguards against resource exhaustion.
Because parsing is iterative and O(n), no limit is needed for
safety; these bound memory and time on hostile input. Every limit that
is exceeded yields a limit diagnostic (Y2xxx) whose help names the
setting to raise, e.g. raise Limits::max_depth (currently 128).
Three profiles:
| Limit | Limits::default() | Limits::untrusted() | Limits::unlimited() |
|---|---|---|---|
max_depth | 1,024 | 128 | none |
alias_budget | max(10 × own nodes, 1,000,000) | max(2 × own nodes, 100,000) | none |
alias_byte_budget | max(10 × input bytes, 64 MiB) | max(2 × input bytes, 8 MiB) | none |
max_document_size | 256 MiB | 16 MiB | none |
max_scalar_len | 64 MiB | 1 MiB | none |
max_documents | none | 1,000 | none |
max_typed_depth | 128 | 64 | none |
max_diagnostics | 100,000 | 1,000 | none |
The alias budgets bound what Value and serde reads materialise by
expanding aliases (nodes, and bytes beyond the input; see
Limits::alias_byte_budget). They are counted per document and
incrementally from the document’s start, so a read of one node never
refuses what the whole document’s read accepts. Each document of a
stream has its own budgets: reading a whole stream expands at most the
sum of its documents’ budgets, so at most max_documents × the largest
one (with untrusted() and small documents: 1,000 × 100,000 nodes and
1,000 × 8 MiB).
An alias whose whole expansion would exceed a budget is refused before
any of it is replayed, so a refused read costs about the input it read.
A numeric limit of usize::MAX means “no limit”. Use
Limits::untrusted() for servers, webhooks, and APIs that parse input
from untrusted sources.
Limits::unlimited() removes every bound, and it is the only way to
disable the alias budgets (nodes and bytes; alias_budget(usize::MAX)
and alias_byte_budget(usize::MAX) make them unreachable instead). Use
it only for input you trust completely.
Compatibility presets never change limits: safety is not a compat behaviour.
§Examples
use oneyaml_core::Limits;
// A service reading small uploads: the untrusted profile, tightened.
let l = Limits::untrusted()
.max_depth(32)
.max_typed_depth(32)
.max_document_size(64 * 1024)
.max_scalar_len(4 * 1024)
.max_documents(1)
.max_diagnostics(10)
.alias_budget(1_000)
.alias_byte_budget(256 * 1024);
assert_eq!(l.get_max_depth(), 32);
assert_eq!(l.get_max_typed_depth(), 32);
assert_eq!(l.get_max_document_size(), 64 * 1024);
assert_eq!(l.get_max_scalar_len(), 4 * 1024);
assert_eq!(l.get_max_documents(), 1);
assert_eq!(l.get_max_diagnostics(), 10);
// A fixed budget ignores the document's size ...
assert_eq!(l.get_alias_budget(1_000_000), Some(1_000));
assert_eq!(l.get_alias_byte_budget(1 << 30), Some(256 * 1024));
// ... a ratio scales with it, above a floor.
let r = l.alias_budget_ratio(3, 100).alias_byte_budget_ratio(2, 4096);
assert_eq!(r.get_alias_budget(10), Some(100));
assert_eq!(r.get_alias_budget(1_000), Some(3_000));
assert_eq!(r.get_alias_byte_budget(10_000), Some(20_000));
// `unlimited()` is the only profile without alias budgets.
assert_eq!(Limits::unlimited().get_alias_budget(1), None);
assert_eq!(Limits::unlimited().get_alias_byte_budget(1), None);Implementations§
Source§impl Limits
impl Limits
Sourcepub const fn new() -> Limits
pub const fn new() -> Limits
The default profile. Same as Limits::default(), usable in const.
Sourcepub const fn unlimited() -> Limits
pub const fn unlimited() -> Limits
No limits at all, including no alias budget. Only for trusted input.
Sourcepub const fn max_depth(self, depth: usize) -> Limits
pub const fn max_depth(self, depth: usize) -> Limits
Maximum nesting depth of collections, counting block and flow collections together; a top-level collection has depth 1.
Consumed by the parser now; the tree builder, Value/serde, and the
emitter will enforce it too.
Sourcepub const fn alias_budget(self, nodes: usize) -> Limits
pub const fn alias_budget(self, nodes: usize) -> Limits
A fixed alias budget: at most nodes nodes may be produced by alias
expansion (including merge keys through aliases).
Applies to Value and serde only, never to Document.
Sourcepub const fn alias_budget_ratio(self, factor: usize, floor: usize) -> Limits
pub const fn alias_budget_ratio(self, factor: usize, floor: usize) -> Limits
A ratio alias budget: max(factor × own nodes, floor), where “own
nodes” is the number of nodes the document itself contains.
Applies to Value and serde only, never to Document.
Sourcepub const fn alias_byte_budget(self, bytes: usize) -> Limits
pub const fn alias_byte_budget(self, bytes: usize) -> Limits
A fixed alias byte budget: at most bytes bytes may
be materialised by alias expansion beyond the input.
Counted per document, incrementally, by Value and serde reads only
(never by Document, which expands nothing):
- every scalar value, key, and tag that alias expansion copies (merge keys through aliases included), in full;
- each tag use’s
%TAGprefix, which a tag shares rather than copies but which its logical text (and so any output of it) repeats; - the text of a
%TAGtag joined into an enum variant name that names no variant of the target (#[serde(other)], hand-written visitors).
Exceeding it is Y2009 (code::ALIAS_BYTE_BUDGET_EXCEEDED).
Independent of Limits::alias_budget, which counts nodes.
Sourcepub const fn alias_byte_budget_ratio(
self,
factor: usize,
floor: usize,
) -> Limits
pub const fn alias_byte_budget_ratio( self, factor: usize, floor: usize, ) -> Limits
A ratio alias byte budget: max(factor × input bytes, floor), where
“input bytes” is how much of the document (from its start, --- or
its first content) has been read at the point of the charge, so a
read of one node has the budget the whole read has at the same
place. See Limits::alias_byte_budget for what is charged.
Sourcepub const fn max_document_size(self, bytes: usize) -> Limits
pub const fn max_document_size(self, bytes: usize) -> Limits
Maximum input size in bytes (the whole stream, after decoding to UTF-8).
Consumed by the parser now; readers will also check it before reading.
Sourcepub const fn max_scalar_len(self, bytes: usize) -> Limits
pub const fn max_scalar_len(self, bytes: usize) -> Limits
Maximum length in bytes of one scalar’s value (after escapes and folding). Consumed by the parser.
Sourcepub const fn max_documents(self, documents: usize) -> Limits
pub const fn max_documents(self, documents: usize) -> Limits
Maximum number of documents in one stream. Consumed by the parser.
Sourcepub const fn max_typed_depth(self, depth: usize) -> Limits
pub const fn max_typed_depth(self, depth: usize) -> Limits
Maximum nesting of collections that serde visits through the target
type’s own Deserialize impls, i.e. how deep a recursive user
type such as struct Tree(Vec<Tree>) may follow the input.
serde’s visitor model recurses once per such level, so this bounds
the native stack: measured at about 1.9 KiB per level in release
builds and 7.4 KiB in debug builds (examples/stack_probe.rs in
oneyaml; the cost depends on the target type). The defaults (128;
64 untrusted) fit a 2 MiB thread stack in both. Before raising it,
make sure the deserializing thread’s stack holds max_typed_depth ×
the per-level cost, e.g. run on a thread built with
std::thread::Builder::stack_size; Limits::unlimited() removes
the bound entirely, so only use it with non-recursive targets or
input you trust. Value targets (and Value fields) are built
iteratively and are bounded only by max_depth.
Sourcepub const fn max_diagnostics(self, diagnostics: usize) -> Limits
pub const fn max_diagnostics(self, diagnostics: usize) -> Limits
Maximum number of diagnostics (errors and warnings together) one parse or read keeps.
The budget is per stream, shared by errors and warnings, and
counts every kind (decoding, settings, syntax, meaning). A list of
diagnostics keeps its first max_diagnostics entries in the order
it reports them; when more were found, it also keeps the first
error (whatever its place: it is the one a strict parse fails with)
and then ends with one summary warning, Y2010
(code::DIAGNOSTICS_SUPPRESSED): “N more diagnostics suppressed”,
located where the first suppressed one was, whose help names this
setting. The summary does not count toward the limit (a capped list
holds at most max_diagnostics + 2 entries) and N is exact.
Exceeding the limit is never an error by itself: strict parses still
fail exactly at the first error, and only then.
Meaning never depends on it: what a read accepts or
refuses, has_errors, restricted mode, which edits are accepted, and
a read’s cost are the same under every limit. Past it, the syntax
tree keeps a diagnostic as a 32-byte skeleton (code, severity,
spans) without its texts, and an error reported from there has its
code’s summary for a message. Paths that discard warnings build at most 16.
Sourcepub const fn get_max_diagnostics(&self) -> usize
pub const fn get_max_diagnostics(&self) -> usize
The max_diagnostics limit (usize::MAX: none).
Sourcepub const fn get_max_typed_depth(&self) -> usize
pub const fn get_max_typed_depth(&self) -> usize
The max_typed_depth limit (usize::MAX: none).
Sourcepub const fn get_max_depth(&self) -> usize
pub const fn get_max_depth(&self) -> usize
The max_depth limit (usize::MAX: none).
Sourcepub const fn get_alias_budget(&self, own_nodes: usize) -> Option<usize>
pub const fn get_alias_budget(&self, own_nodes: usize) -> Option<usize>
The alias budget for a document with own_nodes nodes, or None
when the budget is disabled (Limits::unlimited()).
Sourcepub const fn get_alias_byte_budget(&self, input_bytes: usize) -> Option<usize>
pub const fn get_alias_byte_budget(&self, input_bytes: usize) -> Option<usize>
The alias byte budget after input_bytes bytes of the document, or
None when it is disabled (Limits::unlimited()).
Sourcepub const fn get_max_document_size(&self) -> usize
pub const fn get_max_document_size(&self) -> usize
The max_document_size limit in bytes (usize::MAX: none).
Sourcepub const fn get_max_scalar_len(&self) -> usize
pub const fn get_max_scalar_len(&self) -> usize
The max_scalar_len limit in bytes (usize::MAX: none).
Sourcepub const fn get_max_documents(&self) -> usize
pub const fn get_max_documents(&self) -> usize
The max_documents limit (usize::MAX: none).