Skip to content

Decode numbers from a reader whose buffer is smaller than them - #54

Merged
lalinsky merged 1 commit into
mainfrom
take-int-fast-path
Oct 16, 2026
Merged

lalinsky merged 1 commit into
mainfrom
take-int-fast-path

Conversation

@lalinsky

Copy link
Copy Markdown
Owner

takeInt checked that the reader's buffer could hold the integer before calling Reader.takeInt. On a miss, Reader.takeInt rebases, and rebasing asserts that the bytes fit in the buffer. As a result, a streaming reader with a buffer under 8 bytes failed every u64 or f64 with ReaderBufferTooSmall, even though nothing needs those bytes to be buffered together.

takeInt now reads straight from the buffer when the bytes are all there. Otherwise a cold, out-of-line path copies them out with readSliceAll, which works across refills. ReaderBufferTooSmall is now only returned for strings that have to be buffered whole, such as struct field names, and its doc comment says so.

The test that expected ReaderBufferTooSmall for 1–7 byte buffers now expects those buffers to decode. It uses as_array, so field names don't get in the way. The test fails on main and passes here. The test for a field name longer than the buffer still expects the error.

Performance

This is not a performance change. I expected the fast path to save the guard's compare, but it doesn't. On canada (floats), decoding takes 3.74 M instructions per op here against 3.63 M on main. Wall time on this i7-6700 swings between −12% and +22% depending on the binary, which is code layout, not this change. Twitter and small are within noise.

The real per-value cost is elsewhere. The reader's seek, end and buffer pointer live on the stack and are reloaded after every store into the output. Marking the reader noalias doesn't change that. Fixing it would need a decode path that keeps the cursor in a local variable.

Tests pass on 0.16 and 0.17.

takeInt guarded Reader.takeInt with a check that the reader's buffer can
hold the integer, because on a miss takeInt rebases, which asserts that.
So a streaming reader with a buffer under 8 bytes failed every u64 or f64
with ReaderBufferTooSmall, even though nothing needs those bytes to be
buffered together.

Read the integer straight from the buffer when it is all there, and copy
it out with readSliceAll otherwise, which works across refills.
ReaderBufferTooSmall is now only returned for strings that have to be
buffered whole, such as struct field names.
@coderabbitai

coderabbitai Bot commented Oct 16, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 30 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 721edaa8-028f-4988-82d8-8ef8eb7cb0dd
📥 Commits

Reviewing files that changed from the base of the PR and between 343a2be and 210f7d7.

📒 Files selected for processing: 3
  • CHANGELOG.md
  • src/msgpack.zig
  • src/utils.zig
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lalinsky
lalinsky merged commit de6c29c into main Oct 16, 2026
5 checks passed
@lalinsky
lalinsky deleted the take-int-fast-path branch October 16, 2026 20:35
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