Repository navigation
Conversation
A parsed message decodes a string field on the first getter call and creates a new String each time. An application that reads the same names, codes or keys in every message cannot intern them, because the raw bytes are private. The new stringDecoder option names a class whose static decode(ByteBuf, int, int) method then decodes every lazy string. If the option is not set, the generated code does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WUshTFQ4hEUxEQAySamFiV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #56
Motivation
A parsed message creates a new
Stringeach time it decodes a lazy string field. Applications that read the same names, codes, or keys in every message cannot intern them, because the raw bytes are private. See #56 for numbers.Change
stringDecoder: a fully qualified class name withpublic static String decode(ByteBuf buffer, int index, int length).<stringDecoder>com.example.StringInterner</stringDecoder>lightproto { stringDecoder = 'com.example.StringInterner' }LightProtoGenerator.generate(...)overload. The old overload passesnull.LightProtoCodec.readString(ByteBuf, int, int), and the codec is copied into each generated package. When the option is set, the generator replaces the body of that method with a call to the user'sdecode(). This covers singular and repeated string fields, map keys and values,materialize(),copyFrom(), andequals(). JSON and TextFormat parsing use a different reader, so they do not use it.testsmodule protos withmasterand with this branch, anddiff -rfound no difference.Tests
tests: a second plugin execution generatesstring_decoder.protowith the option set.TestStringInterneris a small reference decoder: a bounded, lock-free, direct-mapped table keyed by a hash of the bytes.StringDecoderTest(6 tests):materialize()andcopyFrom()use the decoder, and the results survive a zeroed buffer;clear()and reuse work;mvn clean installon JDK 21 passes: 605 tests in eachtestsrun (default and-XX:-CompactStrings), 0 failures.Open points for review
readString()and did not pass the option to each field class. That keeps the generator change to 6 lines. If you prefer the option to appear at each call site, I can change it.materialize()decodes a singular string again after its getter has already decoded it. It checks the buffer index and not the cached value. So the README says thatdecode()is "usually" called once for each value. A fix would change the default output, so I left it for a separate change.🤖 Generated with Claude Code
https://claude.ai/code/session_01WUshTFQ4hEUxEQAySamFiV