MCPcopy Index your code
hub / github.com/HebiRobotics/QuickBuffers

github.com/HebiRobotics/QuickBuffers @1.4

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.4 ↗ · + Follow
1,969 symbols 6,431 edges 108 files 487 documented · 25%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

QuickBuffers - Fast Protocol Buffers without Allocations

Build Protobuf Conformance Tests Protobuf Conformance Tests Maven Central

QuickBuffers is a Java implementation of Google's Protocol Buffers that has been developed for low latency use cases in zero-allocation environments. It has no external dependencies, and the API follows Protobuf-Java where feasible to simplify migration.

The main highlights are

  • Allocation-free in steady state. All parts of the API are mutable and reusable.
  • No reflections. GraalVM native-images and R8/ProGuard obfuscation (config) are supported out of the box
  • Faster encoding and decoding speed
  • Smaller code size than protobuf-javalite
  • Built-in JSON marshalling compliant with the proto3 mapping
  • Improved order for optimized sequential memory access
  • Optional accessors as an opt-in feature (java8)

QuickBuffers passes all proto2 conformance tests and is compatible with all Java versions from 6 through 20 as well as Android. Proto3 messages can be generated and are wire compatible, but so far the behavioral differences have not been explicitly added due to some proto3 design decisions that have kept us from using it. Current limitations include

  • Services are not implemented
  • Extensions are embedded directly into the extended message, so support is limited to generation time.
  • Well-known proto3 types such as Timestamp and Duration are not special cased in JSON marshalling
  • Unsigned integer types are JSON encoded as signed integer numbers

Getting started

In order to use QuickBuffers you need to generate messages and add the corresponding runtime dependency. The runtime can be found at the Maven coordinates below.

<properties>
  <quickbuf.version>1.3.3</quickbuf.version>
  <quickbuf.options>indent=4,allocation=lazy,extensions=embedded</quickbuf.options>
</properties>
<dependency>
  <groupId>us.hebi.quickbuf</groupId>
  <artifactId>quickbuf-runtime</artifactId>
  <version>${quickbuf.version}</version>
</dependency>

The message generator protoc-gen-quickbuf is set up as a plugin for the protocol buffers compiler protoc. You can install one of the pre-built packages and run:

protoc-quickbuf --quickbuf_out=${options>:<outputDir> <protoFiles>

or use a protoc-gen-quickbuf-${version}-${arch}.exe plugin binary with an absolute pluginPath:

protoc --plugin-protoc-gen-quickbuf=${exePath} --quickbuf_out=${options>:<outputDir> <protoFiles>

or build messages in Maven using the protoc-jar-maven-plugin:



<plugin>  
  <groupId>com.github.os72</groupId>
  <artifactId>protoc-jar-maven-plugin</artifactId>
  <version>3.11.4</version>
  <executions>
    <execution>
      <phase>generate-sources</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <protocVersion>3.21.12</protocVersion>

        <outputTargets>
          <outputTarget>
            <type>quickbuf</type>
            <pluginArtifact>us.hebi.quickbuf:protoc-gen-quickbuf:${quickbuf.version}</pluginArtifact>
            <outputOptions>${quickbuf.options}</outputOptions>
          </outputTarget>
        </outputTargets>

      </configuration>
    </execution>
  </executions>
</plugin>

The generator features several options that can be supplied as a comma-separated list. The default values are marked bold.

Option Value Description
indent 2, 4, 8, tab sets the indentation in generated files
replace_package (pattern)=replacement replaces the Java package of the generated messages to avoid name collisions with messages generated by --java_out.
input_order quickbuf, number, none improves decoding performance when parsing messages that were serialized in a known order. number matches protobuf-java, and none disables this optimization (not recommended).
output_order quickbuf, number number matches protobuf-java serialization to pass conformance tests that require binary equivalence (not recommended).
store_unknown_fields false, true generates code to retain unknown fields that were encountered during parsing. This allows messages to be routed without losing information, even if the schema is not fully known. Unknown fields are stored in binary form and are ignored in equality checks.
enforce_has_checks false, true throws an exception when accessing fields that were not set
allocation eager, lazy, lazymsg changes the allocation strategy for nested types. eager allocates up-front and results in fewer runtime-allocations, but it may be wasteful and prohibits recursive type declarations. lazy waits until the field is actually needed. lazymsg acts lazy for nested messages, and eager for everything else.
extensions disabled, embedded embedded adds extensions from within a single protoc call directly to the extended message. This requires extensions to be known at generation time. Some plugins may do a separate request per file, so it may require an import to combine multiple files.
java8_optional false, true creates tryGet methods that are short for return if(hasField()) ? Optional.of(getField()) : Optional.absent(). Requires a runtime with Java 8 or higher.
gen_descriptors false, true creates descriptor information for integrating with reflection in existing tools

Reading and writing messages

We tried to keep the public API as close to Google's protobuf-java as possible, so most use cases should require very few changes. The Java related file options are all supported and behave the same way.

// .proto definition
message RootMessage {
  optional string text = 1;
  optional NestedMessage nested_message = 2;
  repeated Person people_list = 3;
}

message NestedMessage {
  optional double value = 1;
}

message Person {
  optional uint32 id = 1;
  optional string name = 2;
}

The main difference is that there are no extra builder classes and that all message contents are mutable. The getMutable() accessors set the has flag and provide access to the nested references.

// Use fluent-style to set values
RootMessage msg = RootMessage.newInstance()
        .setText("Hello World");

// Use getMutable() to set nested messages
msg.getMutableNestedMessage()
        .setValue(1.0);

// Write repeated values into the internally allocated list
RepeatedMessage<Person> people = msg.getMutablePeopleList().reserve(4);
for (int i = 0; i < 4; i++) {
    Person person = people.next()
        .setId(i)
        .setName("person " + i);
}

Messages can be read from a ProtoSource and written to a ProtoSink. newInstance instantiates optimized implementations for accessing contiguous blocks of memory such as byte[] and ByteBuffer. Reads and writes do not modify the ByteBuffer state, so positions and limits need to be manually if needed.

// Convenience wrappers
byte[] buffer = msg.toByteArray();
RootMessage result = RootMessage.parseFrom(buffer);
assertEquals(result, msg);

The internal state can be reset with the setInput and setOutput methods. ProtoMessage::getSerializedSize sets an internally cached size, so it should always be called before serialization if there were any changes.

 // Reusable objects
byte[] buffer = new byte[512];
ProtoSink sink = ProtoSink.newArraySink();
ProtoSource source = ProtoSource.newArraySource();

// Stream messages
for (int i = 0; i < 100; i++) {
    int length = msg.getSerializedSize();
    msg.writeTo(sink.setOutput(buffer, 0, length));
    result.clearQuick().mergeFrom(source.setInput(buffer, 0, length));
}

Additionally, there are also (non-optimized) convenience wrappers for InputStream, OutputStream, and ByteBuffer.

ProtoSink.newInstance(new ByteArrayOutputStream());
ProtoSource.newInstance(new ByteArrayInputStream(bytes));

Keep in mind that mutability comes at the cost of thread-safety, so contents should be cloned with ProtoMessage::clone or copied with ProtoMessage::copyFrom before being passed to another thread.

Direct Source/Sink

Depending on platform support for sun.misc.Unsafe, the DirectSource and DirectSink implementations allow working with off-heap memory. This is intended for reducing unnecessary memory copies when working with direct NIO buffers. Besides not needing to copy data, there is no performance benefit compared to working with heap arrays.

// Write to direct buffer
ByteBuffer directBuffer = ByteBuffer.allocateDirect(msg.getSerializedSize());
ProtoSink directSink = ProtoSink.newDirectSink();
msg.writeTo(directSink.setOutput(directBuffer));
directBuffer.limit(directSink.getTotalBytesWritten());

// Read from direct buffer
ProtoSource directSource = ProtoSource.newDirectSource();
RootMessage result = RootMessage.parseFrom(directSource.setInput(directBuffer));
assertEquals(msg, result);

JSON Source/Sink

ProtoMessages also support reading from and writing to JSON as specified in the proto3 mapping.

// Set some contents
RootMessage msg = RootMessage.newInstance();
msg.setText("👍 QuickBuffers \uD83D\uDC4D");
msg.getMutablePeopleList().next()
    .setId(0)
    .setName("First Name");
msg.getMutablePeopleList().next()
    .setId(1)
    .setName("Last Name");

// Print as prettified json
System.out.println(msg);

The default toString method for all messages returns prettified json. The above prints:

{
  "text": "👍 QuickBuffers 👍",
  "peopleList": [{
      "id": 0,
      "name": "First Name"
    }, {
      "id": 1,
      "name": "Last Name"
    }]
}

More fine grained control is exposed via the JsonSink and JsonSource interfaces.

```Java // json options JsonSink sink = JsonSink.newInstance() .setPrettyPrinting(false)

Extension points exported contracts — how you extend this code

Utf8Decoder (Interface)
Decoder for parsing utf8 bytes into Strings @author Florian Enner @since 26 Nov 2019 [5 implementers]
quickbuf-runtime/src/main/java/us/hebi/quickbuf/Utf8Decoder.java
DoubleEncoder (Interface)
(no doc) [5 implementers]
quickbuf-runtime/src/main/java/us/hebi/quickbuf/Schubfach.java
ProtoEnum (Interface)
@author Florian Enner @since 17 Nov 2019 [1 implementers]
quickbuf-runtime/src/main/java/us/hebi/quickbuf/ProtoEnum.java
MessageFactory (Interface)
Factory interface for creating messages @author Florian Enner @since 15 Aug 2019
quickbuf-runtime/src/main/java/us/hebi/quickbuf/MessageFactory.java
FloatEncoder (Interface)
(no doc)
quickbuf-runtime/src/main/java/us/hebi/quickbuf/Schubfach.java

Core symbols most depended-on inside this repo

newInstance
called by 184
quickbuf-runtime/src/main/java/us/hebi/quickbuf/JsonSink.java
get
called by 121
benchmarks/src/main/java/protos/benchmarks/flatbuffers/fb/Bar.java
add
called by 118
quickbuf-runtime/src/main/java/us/hebi/quickbuf/RepeatedEnum.java
toByteArray
called by 91
quickbuf-runtime/src/main/java/us/hebi/quickbuf/ProtoMessage.java
named
called by 87
protoc-gen-quickbuf/src/main/java/us/hebi/quickbuf/generator/FieldGenerator.java
clear
called by 74
quickbuf-runtime/src/main/java/us/hebi/quickbuf/JsonSink.java
getTypeName
called by 66
protoc-gen-quickbuf/src/main/java/us/hebi/quickbuf/generator/RequestInfo.java
setInput
called by 61
quickbuf-runtime/src/main/java/us/hebi/quickbuf/ArraySource.java

Shape

Method 1,798
Class 158
Interface 8
Enum 5

Languages

Java100%

Modules by API surface

quickbuf-runtime/src/main/java/us/hebi/quickbuf/ProtoSink.java155 symbols
quickbuf-runtime/src/main/java/us/hebi/quickbuf/ProtoSource.java118 symbols
quickbuf-runtime/src/main/java/us/hebi/quickbuf/JsonSource.java100 symbols
quickbuf-runtime/src/main/java/us/hebi/quickbuf/JsonSink.java87 symbols
protoc-gen-quickbuf/src/main/java/us/hebi/quickbuf/generator/RequestInfo.java69 symbols
quickbuf-runtime/src/main/java/us/hebi/quickbuf/JsonEncoding.java63 symbols
quickbuf-runtime/src/main/java/us/hebi/quickbuf/ArraySink.java58 symbols
quickbuf-runtime/src/test/java/us/hebi/quickbuf/ProtoFailTests.java50 symbols
quickbuf-runtime/src/main/java/us/hebi/quickbuf/ByteUtil.java50 symbols
quickbuf-runtime/src/main/java/us/hebi/quickbuf/ArraySource.java45 symbols
quickbuf-runtime/src/test/java/us/hebi/quickbuf/ProtoTests.java43 symbols
protoc-gen-quickbuf/src/main/java/us/hebi/quickbuf/generator/FieldGenerator.java30 symbols

For agents

$ claude mcp add QuickBuffers \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact