Expand description
Runtime-configurable safety limits (omnist-spec section 2.4, D-9 to D-13).
The spec requires every implementation to enforce a finite limit on nesting depth, node count and integer digits, lets it pick the numbers (D-10), and requires it to document them (D-11). It also says an implementation SHOULD enforce a finite maximum input size in bytes (D-23), which this port does. This module is where this port documents them and where a caller changes them.
| Limit | Default | Ceiling | Field |
|---|---|---|---|
| Nesting depth | DEFAULT_MAX_DEPTH (200) | MAX_DEPTH_CEILING (250) | Limits::max_depth |
| Node count (containers) | DEFAULT_MAX_NODES (1 000 000) | MAX_NODES_CEILING (10 000 000) | Limits::max_nodes |
| Integer digits | DEFAULT_MAX_INT_DIGITS (4 300) | MAX_INT_DIGITS_CEILING (43 000) | Limits::max_int_digits |
| Input size, bytes | DEFAULT_MAX_INPUT_BYTES (64 MiB) | MAX_INPUT_BYTES_CEILING (1 GiB) | Limits::max_input_bytes |
The default of each of the first three is the spec’s reference default;
the spec names no default for the input size (D-24), so 64 MiB is this
port’s own choice: it is the cap that bounds parse cost, and the section
on the input size below says how it was chosen. The ceilings are
this port’s own (the spec recommends none for these three): a limit is a
denial-of-service bound, not a tuning knob without an upper end, and the
depth ceiling in particular keeps the recursive readers inside a thread’s
stack: measured with a 2 MB thread stack, the JSON, OML, YAML and XML
readers and Doc handle 300 levels in a debug build and 1 000 in a
release build, but overflow at 400 in debug, so 250 is safe in both
profiles. A caller on a smaller stack than 2 MB must lower the depth.
As for crate::formats::yaml::YamlReadOptions, 0 selects the default
(a zero or unset value never widens a limit) and a value above its
ceiling is refused by Limits::validate, never clamped.
A node is a container (an edge list), as D-9 counts them: keys and scalar
values are not nodes, so a flat mapping of any number of scalar entries is
one node. A document whose deepest leaf sits at depth max_depth is
accepted and one level deeper is refused; likewise for the other two
(a count or digit length equal to the limit is accepted).
Exceeding a limit raises document.limit.depth, document.limit.nodes,
document.limit.int-digits or document.limit.input-size as a
crate::error::DocumentError (E-11): depth and node count are
properties of the document as a whole and carry path $, an over-long
integer carries the Document path of that integer, and an input over the
size limit carries $ too (it is a property of the input as a whole).
§The input size (D-23, D-24)
The size is bytes of the input as received, not characters: \u{e9}
counts as two, and a leading byte-order mark counts as three, because the
length is taken before the mark is stripped (D-15) and before any
decoding or parsing. The check is the first thing every reader does, so
document.limit.input-size is reported ahead of parse.invalid-encoding,
of a doubled-BOM error and of every other diagnostic. An input of exactly
the maximum is accepted. It applies to all five formats, to a YAML input
with no alias included, and to crate::document::Doc::from_format for
any registered format; the CLI also stops reading a file or stdin at the
maximum plus one byte instead of buffering the rest (--max-input-bytes).
The default, 64 MiB, is chosen to bound the cost of the slowest codec
library, not to promise that an input that large parses quickly: the
spec says a cap bounds parse cost without making any parse fast, and
asks an implementation to measure its slowest codec at the size it
chooses (D-24). docs/limitations.md records the measurement and the
behaviour change: before 0.9.0-alpha a reader took any size.
Only Documents read from text or bytes are bounded: an OSD schema text is
not (D-25 asks for a schema-size bound but gives it no code), and a
Document built from native values has no input bytes.
The limits apply to every route into the model: the *_with readers of
each format (crate::oml::read_oml_with,
crate::formats::json::read_json_with,
crate::formats::yaml::YamlReadOptions::with_limits,
crate::formats::toml::read_toml_with,
crate::formats::xml::read_xml_with) and the Document constructors
crate::document::Doc::of_with and crate::document::Doc::from_raw_with.
A crate::document::Doc remembers the limits it was built under, so a
later add/set on it enforces the same ones.
Structs§
- Limits
- The safety limits of spec section 2.4 (nesting depth, node count, integer
digits) and the maximum input size of D-23, taken by the
*_withreaders and, for the first three, bycrate::document::Doc::from_raw_with.
Constants§
- DEFAULT_
MAX_ DEPTH - The reference default for the maximum nesting depth (spec section 2.4).
- DEFAULT_
MAX_ INPUT_ BYTES - The default maximum input size in bytes (spec D-23, D-24): 64 MiB. The spec names no default; this port’s is chosen in the module documentation.
- DEFAULT_
MAX_ INT_ DIGITS - The reference default for the maximum integer digits (spec section 2.4).
- DEFAULT_
MAX_ NODES - The reference default for the maximum node count (spec section 2.4).
- MAX_
DEPTH_ CEILING - The largest maximum nesting depth
Limits::validateaccepts. - MAX_
INPUT_ BYTES_ CEILING - The largest maximum input size
Limits::validateaccepts: 1 GiB. - MAX_
INT_ DIGITS_ CEILING - The largest maximum integer digit count
Limits::validateaccepts. - MAX_
NODES_ CEILING - The largest maximum node count
Limits::validateaccepts.
Functions§
- input_
size_ error - The D-23 refusal:
document.limit.input-sizeat$(the size is a property of the input as a whole, E-4a).hintsays how to raise the maximum, in the caller’s own terms (a library option, a CLI flag).