fix(view): preserve packed closed-enum unknowns#252
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
iainmcgin
left a comment
There was a problem hiding this comment.
[claude code] Thanks for tackling this — the diagnosis is right (the view path dropped unrecognized packed closed-enum values that the owned path preserves), and routing them into the unknown set as individual varints exactly mirrors the owned path, including the as u64 round-trip of negative values. Three things need addressing before this can land:
-
Stale base. The branch predates #184's rework of
UnknownFieldsView— on currentmainit carriesraw_spans+last_tail+to_owned_allowancewith span coalescing, andto_ownedthreads a budgetedDecodeContextrather than callingdecode_unknown_fieldwith a bare recursion limit. GitHub reports the PR as conflicting (which is also why CI hasn't run — thepull_requestworkflow doesn't fire on conflicting PRs). Please rebase onto currentmain; theview.rshalf of the fix needs reworking against that machinery. -
Unknown-field budget bypass. On current
main,push_recordconsumesctx.register_unknown_field()— the memory-amplification bound added in #184 (a DoS guard). The newpush_ownedpath doesn't consume the allowance at decode time, and the owned fields are appended into_ownedoutside the budget. A packed blob consisting entirely of unknown closed-enum varints is precisely the amplification shape the budget exists for (each 1-byte element becomes a ~16+ byte ownedUnknownField). Synthetic unknowns need to draw from the same allowance. -
Wire-order parity. Storing synthetic unknowns in a separate owned store that is written/converted after all raw spans means a message with a packed-enum unknown at field N followed on the wire by a genuinely-unknown field M re-encodes as
[M, N]from the view path but[N, M]from the owned path — a byte-order divergence from the parity goal that the new test asserts in the single-field case. Either interleave in wire order or document the limitation explicitly; and either way please extend the tests: assert!view.__buffa_unknown_fields.is_empty()in the round-trip test (so a regression that drops the unknowns but happens to re-encode identically can't pass), and add a mixed-unknowns parity case.
The codegen half (the closed_enum_view_unknown_route helper mirroring impl_message.rs's owned route, and keeping closed_enum_decode for map values) looks right and should survive the rebase mostly intact. Happy to re-review after the rebase.
Ohhh I dint see that PR merged - thanks - I'll let you know after rebase |
5c10388 to
172f045
Compare
|
@iainmcgin rebased this onto current Changes from the review:
Local checks:
Ready for re-review. |
Closed proto2 enums already preserve unknown values on the owned path, but the packed repeated view path was still dropping them.
This keeps those packed unknown values by letting
UnknownFieldsViewhold synthetic unknown fields for cases where there is no borrowable per-element wire span.I also added a regression test for the packed repeated view case and removed the corresponding item from the README limitations list.
Proof:
mainin a clean worktree:cargo test -p buffa-test 'tests::closed_enum::test_view_closed_enum_repeated_packed_unknown_preserved' -- --exactcargo test -p buffa-test 'tests::closed_enum::test_view_closed_enum_repeated_packed_unknown_preserved' -- --exactcargo test -p buffa 'view::tests::unknown_fields_view_to_owned_includes_owned_fields' -- --exactcargo clippy --workspace --all-targets -- -D warningsandcargo test --workspace