Skip to content

fix: honor registered ext coders for named non-struct types across encode/decode and stream APIs (#55) - #117

Open
shamaton wants to merge 2 commits into
mainfrom
bug/issue-55
Open

fix: honor registered ext coders for named non-struct types across encode/decode and stream APIs (#55)#117
shamaton wants to merge 2 commits into
mainfrom
bug/issue-55

Conversation

@shamaton

@shamaton shamaton commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix ext coders registered for named non-struct types (e.g. Go "enum" types like type Role uint8) being silently ignored outside the struct dispatch path, on both Marshal/Unmarshal and the streaming Encoder/Decoder APIs.
  • Applies to top-level values, slice elements, and struct fields.
  • Adds round-trip tests and benchmarks for all four combinations (encode/decode × non-stream/stream).

Background / Motivation

The ext registry (AddExtCoder/AddExtEncoder) accepts any reflect.Type, but was only consulted from the struct dispatch (calcStruct/writeStruct on encode, setStruct on decode). The general dispatch functions (calcSize/create on encode, decode on decode) switch purely on reflect.Kind, so an ext coder registered for a named non-struct type was silently dropped at the top level and inside slices — Marshal fell back to the plain kind encoding and Unmarshal couldn't recover the value, making the round-trip lossy. The same gap existed in the streaming API, which shares none of this dispatch code with the non-streaming path.

Credit: originally reported and root-caused by @youdie006 in #110 (repro, root-cause analysis, and an initial encode-side fix for the non-stream API). This PR takes a different implementation approach to also cover decode, the streaming API, and avoid a performance regression on the non-ext hot path (see below) — closing #110 with thanks for the report.

Changes

  • internal/encoding: kind-partitioned ext encoder registry (atomic.Pointer + copy-on-write update) so calcSize/create only consult ext coders for kinds that actually have one registered, instead of scanning all registered coders for every value.
  • internal/decoding: new tryExtDecode helper, used from both decode() and setStruct, that recognizes ext wire frames via extEndOffset (full boundary validation, so truncated ext frames are still rejected before any custom decoder runs) and dispatches to the first registered decoder whose Go type matches the destination.
  • internal/stream/encoding, internal/stream/decoding: mirror the same designs for the streaming API.

Testing

  • go test ./..., go vet ./..., go build ./... all clean; race detector clean on the new stream decode tests.
  • New round-trip tests: issue55_ext_test.go (public API, non-stream + stream), plus package-level tests in internal/encoding, internal/decoding, internal/stream/encoding, internal/stream/decoding.
  • Benchmarks added under internal/encoding and internal/decoding comparing plain-value and ext-slice encode/decode paths.

Closes #55.

@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.90816% with 134 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.91%. Comparing base (40ed979) to head (697adb6).

Files with missing lines Patch % Lines
internal/encoding/struct.go 69.09% 28 Missing and 6 partials ⚠️
internal/encoding/encoding.go 84.13% 19 Missing and 14 partials ⚠️
internal/stream/encoding/encoding.go 77.14% 13 Missing and 11 partials ⚠️
internal/stream/encoding/ext.go 84.89% 12 Missing and 9 partials ⚠️
internal/encoding/ext.go 92.85% 6 Missing and 4 partials ⚠️
internal/stream/encoding/struct.go 77.77% 2 Missing and 4 partials ⚠️
internal/decoding/ext.go 85.00% 2 Missing and 1 partial ⚠️
internal/stream/decoding/ext.go 85.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #117      +/-   ##
==========================================
- Coverage   94.90%   92.91%   -2.00%     
==========================================
  Files          74       74              
  Lines        6461     7017     +556     
==========================================
+ Hits         6132     6520     +388     
- Misses        191      311     +120     
- Partials      138      186      +48     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: bug Confirmed or likely defect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ext encoders are not used when encoding distinct int types ("enums")

1 participant