diff --git a/advanced/binary-protocol.mdx b/advanced/binary-protocol.mdx index 586d360..b88ae3b 100644 --- a/advanced/binary-protocol.mdx +++ b/advanced/binary-protocol.mdx @@ -654,7 +654,7 @@ pub enum NodeStr<'a> { pub struct NodeRef<'a> { pub tag: NodeStr<'a>, // Borrowed or inline pub attrs: AttrsRef<'a>, // Vec<(NodeStr<'a>, ValueRef<'a>)> - pub content: Option>>, + pub content: Option>, } pub enum NodeContentRef<'a> { @@ -670,6 +670,14 @@ pub enum NodeContentRef<'a> { `NodeStr` replaces the previous `Cow<'a, str>` used in `NodeRef`, `AttrsRef`, `ValueRef`, and `NodeContentRef`. The key difference is that the `Owned` variant uses `CompactString` (inline up to 24 bytes) instead of `String` (always heap-allocated), reducing allocation pressure for the many short protocol strings that can't be statically interned. + + `NodeRef::content` dropped its `Box` — it is now `Option>` rather than `Option>>`. If you match on it with `.as_deref()`, use `.as_ref()` instead. If you hold a `Box>` directly, it still dereferences the same as before. + + The box existed only to keep `NodeRef::content` pointer-sized, but for the commonest node in a fanout — a leaf with `Cow::Borrowed` byte content, which already allocates nothing — the box was the *only* allocation. Removing it cuts allocations per decode by 40% on a fanout (45 → 27) and drops native decode time by 11.2% on both the fanout and large-stanza benchmarks. `size_of::()` grows from 48 to 72 bytes in exchange (on 64-bit platforms), which does not regress any measured shape, including one with no content to begin with. + + `NodeContentRef::Nodes` keeps its own `Box>`: without it, the recursive child-node type would have no finite size. Removing that box isn't part of this change. + + Location: `wacore/binary/src/node.rs:10-106`, `465-469`, `437-441` ### Node nesting depth cap @@ -936,6 +944,12 @@ The `spilled()` method on `SmallVec` reports whether a given node's attrs overfl Location: `wacore/binary/src/node.rs`, `wacore/binary/tests/attrs_inline_alloc.rs` +### Unboxed `NodeRef` content + +`NodeRef::content` (`Option>`) has no `Box` around the inner value — see the note under [Zero-copy decoding](#zero-copy-decoding). On `bench_unmarshal_fanout`, this drops allocations per decode from 45 to 27 and instructions by 9.5% under callgrind (the instrument CodSpeed uses), alongside an 11.2% native wall-time improvement on both the fanout and large-stanza benchmarks. `bench_unmarshal_small`, which carries no content, is unaffected either way — the gain tracks how many nodes in the tree carry content, not a fixed per-node cost. + +Location: `wacore/binary/src/node.rs`, `wacore/binary/benches/binary_benchmark.rs` + ## Common protocol patterns ### IQ (info/query) stanzas