Skip to content

view: zero-copy Bytes in to_owned via slice_ref of source buffer#74

Merged
iainmcgin merged 2 commits intomainfrom
fix/52-view-bytes-slice-ref
Apr 28, 2026
Merged

view: zero-copy Bytes in to_owned via slice_ref of source buffer#74
iainmcgin merged 2 commits intomainfrom
fix/52-view-bytes-slice-ref

Conversation

@iainmcgin
Copy link
Copy Markdown
Collaborator

Fixes #52.

Problem

PR #51 backed Any.value (and any field in bytes_fields) with bytes::Bytes so cloning is a refcount bump. But the view→owned path — which production decode pipelines typically hit — was still emitting Bytes::copy_from_slice(self.value), allocating and copying just like the old .to_vec().

Approach

Rather than threading a &'a Bytes through decode_view (which doesn't compose with OwnedView<V> — the Bytes handle moves into self after decode, so a stored reference would dangle), this passes the source buffer at conversion time:

  • New MessageView::to_owned_from_source(&self, source: Option<&Bytes>) -> Self::Owned, with a default impl delegating to to_owned_message (so hand-written impls are unaffected).
  • New runtime helper buffa::view::bytes_from_source(Option<&Bytes>, &[u8]) -> Bytes: returns Bytes::slice_ref when the slice lies within the source (pointer-range check mirroring slice_ref's precondition), Bytes::new() for empty, else copy_from_slice. Slices outside the source (e.g. on a manually-constructed view) fall back to copy rather than panic.
  • Generated to_owned_message() now calls to_owned_from_source(None); the new method body emits bytes_from_source(__buffa_src, ...) for bytes_fields and recurses into nested/repeated/oneof/map message values with to_owned_from_source(__buffa_src).
  • OwnedView::to_owned_message() now calls to_owned_from_source(Some(&self.bytes)), so the 'static wrapper gets zero-copy automatically.

No decode-path changes, no view-struct field additions.

Tests

  • 5 unit tests for bytes_from_source (None, in-range, out-of-range, empty, full-range).
  • End-to-end zero-copy assertions (pointer-range checks, not just byte equality) for Any via both the direct trait path and OwnedView.
  • buffa-test coverage for repeated/optional/oneof/nested-message bytes shapes under use_bytes_type().

Out of scope / follow-ups

  • map<K, bytes> values are still Vec<u8> regardless of bytes_fields (pre-existing; map_to_owned_expr hard-codes .to_vec()).
  • The issue's string_fields suggestion is left for later.

Adds MessageView::to_owned_from_source(Option<&Bytes>) so view→owned
conversion can produce bytes::Bytes fields via Bytes::slice_ref (refcount
bump, no copy) when the source buffer is supplied. OwnedView::to_owned_message
now routes through this with its retained buffer, recovering the headline
zero-copy benefit of #51 on the view→owned path.

Fixes #52.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

…ce sig

The previous ::bytes::Bytes is an absolute crate path, requiring every
consumer crate to depend on `bytes` directly. The conformance crate
doesn't, so its build broke. Generated code must reference bytes via
buffa's re-export, same as ::buffa::bytes::BufMut elsewhere.
@iainmcgin iainmcgin marked this pull request as ready for review April 28, 2026 17:37
@iainmcgin iainmcgin requested a review from rpb-ant April 28, 2026 17:37
@iainmcgin iainmcgin merged commit b4b7005 into main Apr 28, 2026
7 checks passed
@iainmcgin iainmcgin deleted the fix/52-view-bytes-slice-ref branch April 28, 2026 19:02
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AnyView::to_owned_message copies Bytes payload unnecessarily

2 participants