Releases: streamnative/lightproto
v0.7.3
Dependency cleanup
This release shrinks the lightproto-code-generator dependency footprint and removes the transitive log4j 1.2.17 that came in via jibx-tools. Generated code is byte-identical to v0.7.2.
Drop jibx-tools dependency (#8)
jibx-tools was only used for two simple pluralize / depluralize helpers feeding into generated repeated-field accessor names (addItem, getItemsCount, …). The actual logic — ~40 lines of suffix-rule manipulation from org.jibx.util.NameUtilities — is now vendored directly into the code generator (BSD 3-clause, attributed in source). This drops:
org.jibx:jibx-tools1.3.3log4j:log4j1.2.17 (multiple CVEs)- ~10 ancient
org.eclipse.*JDT jars
The vendored behavior is locked in by a new parameterized UtilTest that covers every branch of the rules (including the case-sensitivity quirk where "ANY" pluralizes to "ANYs").
Drop guava dependency (#9)
The code generator used guava for three small utilities: Joiner.on('/').join(...) (replaced by String.join), Maps.newHashMap() (replaced by new HashMap<>()), and CaseFormat snake↔camel conversions (replaced by two small in-tree helpers, verified byte-identical to guava's output for every input shape that appears in the code generator). Removes:
com.google.guava:guava32.0.0-jre- 6 transitive annotation jars (
failureaccess,listenablefuture,jsr305,checker-qual,error_prone_annotations,j2objc-annotations)
Net effect
After these two changes, the lightproto-code-generator runtime dependency tree is just protobuf-java + slf4j-api + roaster. The generated runtime is unchanged.
v0.7.2
What's New
JSON serialization is now opt-in (#7)
Breaking: JSON methods (writeJsonTo, parseFromJson, toJson) are no longer generated by default. To opt back in, set the new generateJson plugin option (Maven <generateJson>true</generateJson>, Gradle generateJson = true), mirroring how generateTextFormat already worked. The package-private LightProtoMessage interface no longer declares the JSON methods, so messages compile cleanly when JSON is disabled.
Required-field getters return defaults instead of throwing (#5)
proto2 required-field getters used to throw IllegalStateException when the field was unset. They now return the type-appropriate default (0 / false / "" / empty bytes / valueOf(0) / lazily-created empty message), matching protobuf-java. Required-field validation still runs at writeTo() and parseFrom() time, so missing required fields are still caught at the message boundary.
bytes field setters no longer consume the source ByteBuf reader index (#6)
Setting a bytes field from a ByteBuf no longer advances the source buffer's readerIndex during serialization, so a message remains safe to re-serialize (e.g., on a gRPC retry) and two fields can safely alias the same backing buffer.
v0.7.0
What's New
Protobuf TextFormat (de)serialization (opt-in)
- New
generateTextFormatplugin option (Maven<generateTextFormat>true</generateTextFormat>, GradlegenerateTextFormat = true). When enabled, every generated message getstoTextFormat(),writeTextFormatTo(StringBuilder), andparseFromTextFormat(String|byte[]|ByteBuf). - Output is canonical multi-line, indented protobuf TextFormat — compatible with
com.google.protobuf.TextFormat.printer()for serialization andTextFormat.merge()for parsing, so existing TextFormat data round-trips. - Cross-package sub-messages share a
ByteBufcursor, so messages that reference types from a different generated package parse correctly (same handoff used by JSON parsing).
Generated equals() / hashCode()
- All generated message types now implement
equals(Object)andhashCode(), making LightProto messages safe to use directly as keys inHashMap/HashSetand to compare withassertEqualsin tests.
v0.6.6
What's New
Cross-package proto import support for JSON parsing
- Fixed JSON deserialization when messages reference types from different protobuf packages (different Java packages)
- Refactored
JsonReaderto useByteBufinternally, eliminating cross-package type incompatibilities
Generated file headers
- All generated
.javafiles now include a header comment with LightProto version and source.protofilename
v0.6.5
What's New
JSON Deserialization (parseFromJson)
Every generated message now supports JSON deserialization with three overloads:
message.parseFromJson(String json);
message.parseFromJson(byte[] jsonBytes);
message.parseFromJson(ByteBuf jsonBuf);The JSON format is fully compatible with protobuf's JsonFormat output:
- lowerCamelCase field names
- int64/uint64 values quoted as strings
- Enum values as string names
- Bytes fields as base64-encoded strings
- Unknown fields silently ignored (forward compatibility)
This enables drop-in replacement of JsonFormat.parser().merge() when migrating from Google Protobuf to LightProto.
Implementation Details
- Lightweight recursive-descent JSON parser in
LightProtoCodecwith no external dependencies - Supports all field types: scalars, strings, bytes, enums, nested messages, repeated fields, and maps
ByteBufoverload avoidsStringallocation when JSON is already in a buffer
Lightproto 0.6.3
Bug Fixes
- Fixed ByteBuf leak in gRPC marshaller — The generated
Marshaller.stream()was allocating a NettyByteBufthat could leak when gRPC failed to close the returnedInputStream(e.g., whenNoopClientStreamorFailingClientStreamsilently discards the message). The marshaller now serializes into anUnpooled.buffer(), extracts the backingbyte[]directly (zero-copy), and wraps it in a GC-safeByteArrayInputStreamsubclass. Even ifclose()is never called, there is no resource leak. (grpc-java#12729)
Improvements
-
Implemented
DrainableandKnownLengthon marshaller InputStream — The stream returned byMarshaller.stream()now implementsio.grpc.Drainableandio.grpc.KnownLength. This enables gRPC'sMessageFramerto use the fastdrainTo()path — a singlewrite(byte[], off, len)call directly into the transport buffer — instead of falling back to byte-by-byteByteStreams.copy(). -
Replaced
sun.misc.Unsafetype references withMethodHandles— The generated codec no longer referencessun.misc.Unsafetypes, avoiding deprecation warnings and ensuring forward compatibility with future JDK releases.
LightProto 0.6.2
LightProto 0.6.2
Bug Fixes
-
Return default values for unset optional fields instead of throwing (#2) — Accessing an unset optional field (proto2) now returns the type-appropriate default value (0, false,
"", empty bytes,valueOf(0)for enums, empty message instance) instead of throwingIllegalStateException. This matches standard Protobuf behavior.IllegalStateExceptionis now only thrown for required fields. Theclear()method also properly resets all field values back to defaults. -
Fix Unsafe string optimizations when
-XX:-CompactStringsis used (#1) — String serialization could produce corrupt output on JVMs running with-XX:-CompactStrings. The Unsafe-based fast path now correctly detects compact string encoding and falls back to the safe path when compact strings are disabled. -
Catch specific exceptions in LightProtoCodec static initializer — The static initializer for Unsafe access now catches specific exception types instead of a broad
Exception, improving debuggability when initialization fails.
Improvements
- Add javadoc to generated gRPC service classes (#3) — All public methods and inner types in generated gRPC service classes now have javadoc comments, including method descriptor getters, stub factories,
AsyncService,ImplBase,Stub, andBlockingStub.
LightProto 0.6.1
LightProto 0.6.1
Improvements
- Make
materialize()a public API for cross-package usage — Renamed the internal_materialize()method tomaterialize()with public visibility, allowing generated messages in different packages to call it on referenced message types. TheparseFrom(buffer, size, eager)overload was removed in favor of explicitmaterialize()calls.
Full Changelog: v0.6.0...v0.6.1
LightProto 0.6.0
LightProto v0.6.0
This release moves LightProto under the StreamNative organization and includes all changes since v0.4.
Proto3 Support
Full proto3 syntax support including implicit field presence, packed-by-default repeated fields, and the optional keyword. Proto2 remains fully supported.
New Field Types
map<K, V>fields — complete support for protobuf map fields with all key/value type combinationsoneoffields — proto2 oneof support with efficient memory layout
gRPC Service Stubs
Generate complete *Grpc.java service stubs from service definitions in .proto files. Includes AsyncService, ImplBase, Stub, BlockingStub, MethodDescriptors, and ServiceDescriptor. Features zero-copy marshaller using Netty pooled direct buffers and cross-package type resolution.
Gradle Plugin
New io.streamnative.lightproto Gradle plugin for build-time code generation, complementing the existing Maven plugin.
Parser Rewrite
Replaced protostuff-parser with protoc descriptor sets for more accurate and complete proto file parsing.
Serialization Performance Overhaul
- Unsafe-based serialization and string deserialization
- Pre-ensureWritable in
writeTo()eliminates per-field capacity checks - ASCII fast-path for string serialization
- Native arrays replace
ArrayListfor repeated fields - Precomputed tag sizes for fixed-size types
- Unrolled VarInt64 decoding
- Cached serialized size after
parseFrom
Eager deserialization support
A new parseFrom(ByteBuf buffer, int size, boolean eager) overload was introduced. Setting eager to true immediately resolves all lazily-deserialized fields (strings, bytes, and nested messages), preventing the resulting object from retaining references to the source ByteBuf. This proves valuable when buffers may be recycled after parsing.
Zero-copy gRPC deserialization
The gRPC marshaller implementation now leverages reflection to access the underlying ByteBuf from gRPC's ByteBufInputStream, deserializing eagerly without creating an intermediate byte[] copy. If the InputStream isn't a ByteBufInputStream, the system reverts to parseFrom(byte[]).
Javadoc generation
Proto source comments are now converted to Javadoc on generated message classes and enum types. All generated accessor methods including get, set, has, clear, add, getCount, and getList now feature Javadoc explaining their functionality.
Java 21+ compatibility
Warnings regarding sun.misc.Unsafe removal on Java 21+ were suppressed to maintain cleaner build output.
Performance: v0.4 → v0.6
| Benchmark | v0.4 | v0.6 | Speedup |
|---|---|---|---|
| ProtoBenchmark Serialize | 8.3 | 22.6 | +172% |
| ProtoBenchmark FillAndSerialize | 5.9 | 12.8 | +117% |
| ProtoBenchmark Deserialize | 16.7 | 19.2 | +15% |
| SimpleBenchmark Serialize | 251.1 | 245.6 | ~same |
| SimpleBenchmark Deserialize | 93.2 | 100.9 | +8% |
| SimpleBenchmark DeserializeReadString | 39.4 | 54.1 | +37% |
| PulsarAPI SerializeBaseCommand | 12.4 | 26.8 | +116% |
| PulsarAPI SerializeMessageMetadata | 6.1 | 10.9 | +79% |
| PulsarAPI DeserializeBaseCommand | 39.1 | 40.3 | +3% |
| PulsarAPI DeserializeMessageMetadata | 14.0 | 14.1 | ~same |
LightProto v0.6 vs Google Protobuf (PulsarAPI)
| Benchmark | Protobuf | LightProto | Speedup |
|---|---|---|---|
| Serialize MessageMetadata | 2.84 | 10.85 | 3.8x |
| Serialize BaseCommand | 15.82 | 26.81 | 1.7x |
| Deserialize MessageMetadata | 4.18 | 14.14 | 3.4x |
| Deserialize BaseCommand | 12.77 | 40.30 | 3.2x |
Dependency Updates
- Bump
com.google.guava:guavafrom 30.1-jre to 32.0.0-jre - Bump
org.apache.maven:maven-corefrom 3.2.5 to 3.8.1