pub enum Value {
Undefined,
Null,
Bool(bool),
Number(f64),
String(String),
Array(Arc<Vec<Value>>),
Object(Arc<IndexMap<String, Value>>),
Text(Text),
ListRef(Arc<ListRef>),
MapRef(Arc<MapRef>),
}Variants§
Undefined
Null
Bool(bool)
Number(f64)
String(String)
Array(Arc<Vec<Value>>)
Shared, not owned. The engine builds a tree by folding each
finished rule’s value into its parent’s container, and with an
owned container that fold copied the whole accumulated subtree –
once per level, so a document nested d deep cost O(d^2). A
depth-1024 document took 398 ms where TypeScript, whose values are
references, took 6 ms. Behind an Arc the fold is a refcount bump
and the build is linear again.
Arc rather than Rc because Value is Send + Sync and has to
stay that way: Options carries Values and is shared as
Arc<Options>, and a parsed value can be sent between threads
today. The atomic lands only on containers, never on a Number or
a String, and one refcount beats copying a map by any measure.
Mutate through Arc::make_mut, which copies only when the
container is genuinely shared.
Object(Arc<IndexMap<String, Value>>)
Text(Text)
ListRef(Arc<ListRef>)
Shared for the reason above, and behind a pointer for the reason
they always were: a ListRef is 112 bytes and a MapRef 152, and
an unboxed variant sets the size of every Value, hence of every
Token and every Rule. Both are niche next to the scalars and
containers the parse loop moves constantly.
MapRef(Arc<MapRef>)
Implementations§
Source§impl Value
impl Value
Sourcepub fn array(values: Vec<Value>) -> Self
pub fn array(values: Vec<Value>) -> Self
Build an array value from an owned vector.
The variant holds an Arc, so that a value folded into a parent
container costs a refcount rather than a copy. Constructing one
still starts from an owned vector, and this is the shorthand for
handing it over.
Sourcepub fn object(entries: IndexMap<String, Value>) -> Self
pub fn object(entries: IndexMap<String, Value>) -> Self
Build an object value from an owned map. See Value::array.
Sourcepub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>>
pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>>
The array behind this value, ready to write to, or None when it
is not an array.
Copies the contents first if anything else still shares them, so a write through this handle is never seen by another holder. That copy is the whole price of sharing, and it is paid only when a value is mutated after being handed on – which the tree build, which only ever folds a finished value upwards, never does.
Sourcepub fn as_object_mut(&mut self) -> Option<&mut IndexMap<String, Value>>
pub fn as_object_mut(&mut self) -> Option<&mut IndexMap<String, Value>>
The map behind this value, ready to write to, or None when it is
not an object. See Value::as_array_mut.
pub fn is_undefined(&self) -> bool
pub fn is_null(&self) -> bool
Sourcepub fn unwrap_undefined(self) -> Value
pub fn unwrap_undefined(self) -> Value
Replace every Undefined in the tree with Null.
Iterative on purpose. The recursive form walked the document with
the call stack, one frame per nesting level, and a strict-JSON
parse of [[[...]]] overflowed the default 8 MiB main-thread stack
– an uncatchable abort – somewhere between 8,000 and 16,000
levels in a release build and between 2,000 and 4,000 in a debug
build. TypeScript’s parse loop is iterative (ts/src/parser.ts,
while (norule !== rule ...)), so this walk was the only part of a
parse whose stack use grew with the document’s depth.
tests/deep_nesting_test.rs pins it.
The walk is also skipped when the tree holds no Undefined, the
common case: rebuilding every container would otherwise copy each
level, since the parse’s root node still shares them.