Skip to content

feat: add next! and prev! for in-place LazyNode traversal#59

Open
mathieu17g wants to merge 1 commit into
JuliaComputing:mainfrom
mathieu17g:feature-next-bang
Open

feat: add next! and prev! for in-place LazyNode traversal#59
mathieu17g wants to merge 1 commit into
JuliaComputing:mainfrom
mathieu17g:feature-next-bang

Conversation

@mathieu17g
Copy link
Copy Markdown

Summary

next(::LazyNode) allocates a fresh LazyNode wrapper on every call. Consumers walking large documents — e.g. extracting every Placemark from a 50 MiB KML — can churn ~1 M wrappers per traversal (~38 MiB cumulative). This PR adds next!(o) / prev!(o) that mutate o in place and return it, or nothing at the document boundary. Functionally equivalent to o = next(o), zero per-step allocation.

Why this is safe

Strictly additive: next / prev are unchanged, callers opt in. The aliasing trade-off (o is the same object across calls, so a retained reference would silently track the new position) is documented inline; the docstring points readers needing a snapshot at LazyNode(o.raw).

Why it matters

Measured on FastKML.jl extracting a DataFrame from a 47 MiB sample KML (~1 M Raw nodes traversed): the per-step allocation site at next(::LazyNode) was contributing ~38 MiB; switching the consumer's traversal loop to next! drops that to zero with no functional change. Independent of (and stackable with) the next_no_xml_space ctx fix in #58.

Verification

Full test suite passes (Julia 1.12), including a new LazyNode next! / prev! testset covering: functional equivalence with next, identity (next!(o) === o), memoization-field reset on advance, nothing at the document boundary, and prev! symmetry.

`next(o::LazyNode)` allocates a fresh `LazyNode` on every call, which
is fine for occasional use but adds up sharply when a downstream
package walks a large document — e.g. extracting all `Placemark`
elements from a 50 MiB KML can allocate ~1 M `LazyNode` wrappers in
the iterator alone (~38 MiB cumulative on a single benchmark run).

Add a strictly-additive in-place pair, `next!(o)` / `prev!(o)`, that
mutates `o` to point at the next/previous node and returns `o`
(or `nothing` at the document boundary). Exported alongside
`next` / `prev`. The aliasing trade-off is documented in the
docstring: callers must not retain references to a previous position
unless they explicitly snapshot with `LazyNode(o.raw)`.

The existing `next` / `prev` methods are unchanged; this is purely
opt-in API surface for hot paths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant