Sanity limits on a document read from elsewhere (spec/14 §9.2, D-137).
Every reader — Visualize.Chart.from_map/2, from_json/2 and
Visualize.Chart.Fragment.from_json/2 — applies these, the defaults when no limits: is
given. They bound what reading and rendering a document can be made to cost; they are
sanity limits, not a render budget.
| Limit | Default | Bounds |
|---|---|---|
depth | 64 | how deeply maps and lists nest, a use site's local and vars included |
items | 100_000 | the entries one list or map holds — a design holds no rows of its own, so this bounds every list a row could be held inline in |
ticks | 1_000 | an axis's tick count |
iterations | 10_000 | a :force step's ticks |
counts | 1_000_000 | bins, samples, n, width, and a grid's size |
The defaults are the system's sanity values, and the host holds the number. A host
loading untrusted input knows its inputs and tightens the limits at its layer:
Visualize.Chart.from_json(json, limits: [items: 5_000, depth: 24]); a limit it does not
name keeps its default.
A fault is {path, {:limit, name, max, given}}, the validator's shape (spec/14 §10.1),
so Visualize.Chart.Validator.format/1 words it:
iex> Visualize.Chart.Limits.check(%{version: 2, frames: %{main: %{axes: [%{scale: :x, ticks: 5000}]}}})
{:error, [{[:frames, :main, :axes, 0, :ticks], {:limit, :ticks, 1000, 5000}}]}
iex> Visualize.Chart.Validator.format({[:frames, :main, :axes, 0, :ticks], {:limit, :ticks, 1000, 5000}})
"frames.main.axes[0].ticks: asks for 5000 ticks; the limit ticks is 1000"
Summary
Functions
:ok, or every depth, items, ticks, iterations and counts fault of a design
map at the current version, structure/2's first; limits as new/1 takes them.
Every ticks, iterations and counts fault of a design map at the current version,
found by walking it with the schema, ordered by path. A variable is not a literal of the
document and is not checked.
The system's sanity values (spec/14 §9.2).
The limits a reader applies: given merged over defaults/0.
Every depth and items fault of any term — a design, a fragment, a parsed JSON
document with string keys — ordered by path.
Types
@type name() :: :depth | :items | :ticks | :iterations | :counts
A limit's name.
@type t() :: %{required(name()) => pos_integer()}
The limits a reader applies, one positive integer per name.
Functions
@spec check(term(), keyword() | map()) :: :ok | {:error, [Visualize.Chart.Validator.error(), ...]}
:ok, or every depth, items, ticks, iterations and counts fault of a design
map at the current version, structure/2's first; limits as new/1 takes them.
@spec counts(term(), t()) :: [Visualize.Chart.Validator.error()]
Every ticks, iterations and counts fault of a design map at the current version,
found by walking it with the schema, ordered by path. A variable is not a literal of the
document and is not checked.
@spec defaults() :: t()
The system's sanity values (spec/14 §9.2).
The limits a reader applies: given merged over defaults/0.
Raises ArgumentError for a name that is not a limit or a value that is not a positive
integer.
iex> Visualize.Chart.Limits.new(items: 10).items
10
@spec structure(term(), t()) :: [Visualize.Chart.Validator.error()]
Every depth and items fault of any term — a design, a fragment, a parsed JSON
document with string keys — ordered by path.
The document itself is depth 1. A struct is a leaf, but a %Visualize.Chart.Use{},
whose local and vars hold fragments. Past the depth limit a branch is reported once,
at the first map or list beyond it, and not walked further.