Skip to content

Add borrowed header-map decoding - #387

Open
datibbaw wants to merge 1 commit into
proxy-wasm:mainfrom
datibbaw:borrowed-header-map-decoding
Open

datibbaw wants to merge 1 commit into
proxy-wasm:mainfrom
datibbaw:borrowed-header-map-decoding

Conversation

@datibbaw

@datibbaw datibbaw commented Nov 3, 2026 •

Copy link
Copy Markdown

Bulk header-map reads currently copy each name and value into separate allocations after the host has transferred the serialized buffer into guest memory. This adds hostcalls::get_serialized_map and an owning map::SerializedMap so callers can keep that buffer and borrow its fields.

The Go SDK already uses shared-buffer views for bulk map decoding. Its DeserializeMap allocates the result slice, then constructs names and values as string views into the serialized buffer. For example, the value decoding uses:

valuePtr := bs[dataIndex : dataIndex+valueSize]
value := *(*string)(unsafe.Pointer(&valuePtr))

That path still allocates the pair slice and requires the initial host-to-guest transfer, but avoids separate allocations and copies for each field. This PR applies the same shared-buffer approach in Rust, with an explicit buffer owner and compiler-checked lifetimes rather than unsafe string conversions. Values stay available as bytes; callers opt into text validation when needed.

For example, acquiring and decoding 20 header pairs currently takes approximately 42 allocations:

  • The serialised buffer,
  • The pair vector, and
  • 40 field allocations

With borrowed values those allocations drop to just the first two. Callers only make copies to e.g. retain values for asynchronous work.

  • pairs() returns borrowed (&str, &[u8]) entries, preserving opaque values without UTF-8 validation.
  • text_pairs() additionally validates values as UTF-8.
  • Both accessors validate bounds, lengths, terminators, trailing data, and name UTF-8, preserving order, duplicates, empty fields, and embedded NULs.
  • Views borrow a separate owner; no self-referential structs or lifetime transmutation are needed. Existing owned APIs and trait convenience methods retain their behavior.

The new hostcall returns host statuses unchanged and rejects a null pointer with nonzero length as InternalFailure. The initial host-to-guest transfer remains; this removes subsequent per-field copies within the guest.

Tests cover:

  • Malformed buffers,
  • Binary values,
  • Text conversion,
  • Null/empty host buffers,
  • Ownership/drop error paths

Allocation tests assert two allocations for acquisition, always, plus byte-valued views at 1, 4, 20, and 64 entries over repeated acquisition/drop cycles.

Signed-off-by: Tjerk Meesters <tmeesters@zendesk.com>
@datibbaw
datibbaw force-pushed the borrowed-header-map-decoding branch from 85e138c to 80a604a Compare November 3, 2026 02:22
@datibbaw
datibbaw marked this pull request as ready for review November 3, 2026 02:42
@datibbaw
datibbaw requested a review from PiotrSikora as a code owner November 3, 2026 02:42
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