{"id":21147622,"url":"https://github.com/hebirobotics/quickbuffers","last_synced_at":"2025-07-09T07:33:06.327Z","repository":{"id":36378719,"uuid":"200694582","full_name":"HebiRobotics/QuickBuffers","owner":"HebiRobotics","description":"Java Protobuf implementation suitable for real-time enviroments","archived":false,"fork":false,"pushed_at":"2024-02-02T11:26:45.000Z","size":2260,"stargazers_count":111,"open_issues_count":1,"forks_count":10,"subscribers_count":9,"default_branch":"main","last_synced_at":"2024-05-10T22:07:50.246Z","etag":null,"topics":["java","protobuf","protocol-buffers"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HebiRobotics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-08-05T16:50:48.000Z","updated_at":"2024-05-10T04:16:41.000Z","dependencies_parsed_at":"2023-12-20T16:16:29.513Z","dependency_job_id":null,"html_url":"https://github.com/HebiRobotics/QuickBuffers","commit_stats":{"total_commits":423,"total_committers":2,"mean_commits":211.5,"dds":0.009456264775413725,"last_synced_commit":"c19faefd3c7ad8a06fab6498d4dff4a07a75f97c"},"previous_names":["hebirobotics/robobuffers"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HebiRobotics%2FQuickBuffers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HebiRobotics%2FQuickBuffers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HebiRobotics%2FQuickBuffers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HebiRobotics%2FQuickBuffers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HebiRobotics","download_url":"https://codeload.github.com/HebiRobotics/QuickBuffers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225506411,"owners_count":17483515,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","protobuf","protocol-buffers"],"created_at":"2024-11-20T09:17:39.651Z","updated_at":"2024-11-20T09:17:40.323Z","avatar_url":"https://github.com/HebiRobotics.png","language":"Java","readme":"\u003c!-- \n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://hebirobotics.github.io/QuickBuffers/icon.png\"  alt=\"QuickBuffers icon\"\u003e\n\u003c/p\u003e\n--\u003e\n\n# QuickBuffers - Fast Protocol Buffers without Allocations\n\n[![Build](https://github.com/HebiRobotics/QuickBuffers/actions/workflows/maven.yml/badge.svg?branch=main)](https://github.com/HebiRobotics/QuickBuffers/actions/workflows/maven.yml)\n[![Protobuf Conformance Tests](https://github.com/HebiRobotics/QuickBuffers/actions/workflows/conformance.yml/badge.svg?branch=main)](https://github.com/HebiRobotics/QuickBuffers/actions/workflows/conformance.yml)\n[![Protobuf Conformance Tests](https://github.com/HebiRobotics/QuickBuffers/actions/workflows/conformance.yml/badge.svg?branch=develop)](https://github.com/HebiRobotics/QuickBuffers/actions/workflows/conformance.yml)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/us.hebi.quickbuf/quickbuf-runtime/badge.svg)](https://maven-badges.herokuapp.com/maven-central/us.hebi.quickbuf/quickbuf-runtime)\n\nQuickBuffers is a Java implementation of [Google's Protocol Buffers](https://developers.google.com/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.\n\nThe main highlights are\n\n * **Allocation-free** in steady state. All parts of the API are mutable and reusable.\n * **No reflections**. GraalVM native-images and R8/ProGuard obfuscation ([config](#proguard-configuration)) are supported out of the box\n * **Faster** encoding and decoding [speed](./benchmarks)\n * **Smaller** code size than protobuf-javalite\n * **Built-in JSON** marshalling compliant with the [proto3 mapping](https://developers.google.com/protocol-buffers/docs/proto3#json)\n * **Improved order** for optimized [sequential memory access](order.md)\n * **Optional accessors** as an opt-in feature (java8)\n\nQuickBuffers passes all [proto2 conformance tests](./conformance) 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](proto3.md) that have kept us from using it. Current limitations include\n\n* [Services](https://developers.google.com/protocol-buffers/docs/proto#services) are not implemented\n* [Extensions](https://developers.google.com/protocol-buffers/docs/proto#extensions) are embedded directly into the extended message, so support is limited to generation time.\n* The [proto files](https://github.com/protocolbuffers/protobuf/tree/main/src/google/protobuf) for [well-known proto3 types](https://protobuf.dev/reference/protobuf/google.protobuf/) such as `timestamp.proto` or `duration.proto`need to be included manually. Their special cased JSON representations are not implemented.\n* Unsigned integer types are JSON encoded as signed integer numbers\n\n## Getting started\n\nIn 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.\n\n```xml\n\u003cproperties\u003e\n  \u003cquickbuf.version\u003e1.4\u003c/quickbuf.version\u003e\n  \u003cquickbuf.options\u003eindent=4,allocation=lazy,extensions=embedded\u003c/quickbuf.options\u003e\n\u003c/properties\u003e\n```\n\n```XML\n\u003cdependency\u003e\n  \u003cgroupId\u003eus.hebi.quickbuf\u003c/groupId\u003e\n  \u003cartifactId\u003equickbuf-runtime\u003c/artifactId\u003e\n  \u003cversion\u003e${quickbuf.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThe 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](https://hebirobotics.github.io/QuickBuffers/download.html) and run:\n\n```sh\nprotoc-quickbuf --quickbuf_out=${options\u003e:\u003coutputDir\u003e \u003cprotoFiles\u003e\n```\n\nor use a [protoc-gen-quickbuf-${version}-${arch}.exe](https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/) plugin binary with an absolute `pluginPath`:\n\n```sh\nprotoc --plugin-protoc-gen-quickbuf=${exePath} --quickbuf_out=${options\u003e:\u003coutputDir\u003e \u003cprotoFiles\u003e\n```\n\nor build messages in Maven using the [protoc-jar-maven-plugin](https://github.com/os72/protoc-jar-maven-plugin):\n\n```xml\n\u003c!-- Downloads protoc w/ plugin and generates messages --\u003e\n\u003c!-- Default settings expect .proto files to be in src/main/protobuf --\u003e\n\u003cplugin\u003e  \n  \u003cgroupId\u003ecom.github.os72\u003c/groupId\u003e\n  \u003cartifactId\u003eprotoc-jar-maven-plugin\u003c/artifactId\u003e\n  \u003cversion\u003e3.11.4\u003c/version\u003e\n  \u003cexecutions\u003e\n    \u003cexecution\u003e\n      \u003cphase\u003egenerate-sources\u003c/phase\u003e\n      \u003cgoals\u003e\n        \u003cgoal\u003erun\u003c/goal\u003e\n      \u003c/goals\u003e\n      \u003cconfiguration\u003e\n        \u003cprotocVersion\u003e3.21.12\u003c/protocVersion\u003e\n\n        \u003coutputTargets\u003e\n          \u003coutputTarget\u003e\n            \u003ctype\u003equickbuf\u003c/type\u003e\n            \u003cpluginArtifact\u003eus.hebi.quickbuf:protoc-gen-quickbuf:${quickbuf.version}\u003c/pluginArtifact\u003e\n            \u003coutputOptions\u003e${quickbuf.options}\u003c/outputOptions\u003e\n          \u003c/outputTarget\u003e\n        \u003c/outputTargets\u003e\n\n      \u003c/configuration\u003e\n    \u003c/execution\u003e\n  \u003c/executions\u003e\n\u003c/plugin\u003e\n```\n\nThe generator features several options that can be supplied as a comma-separated list. The default values are marked bold.\n\n| Option                   | Value                      | Description                                                                                                                                                                                                                                                                                                       |\n|:-------------------------|:---------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **indent**               | **2**, 4, 8, tab           | sets the indentation in generated files                                                                                                                                                                                                                                                                           |\n| **replace_package**      | (pattern)=replacement      | replaces the Java package of the generated messages to avoid name collisions with messages generated by `--java_out`.                                                                                                                                                                                             |\n| **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).                                                                                                                               |\n| **output_order**         | **quickbuf**, number       | `number` matches protobuf-java serialization to pass conformance tests that require binary equivalence (not recommended).                                                                                                                                                                                         |\n| **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.                                                   |\n| **enforce_has_checks**   | **false**, true            | throws an exception when accessing fields that were not set                                                                                                                                                                                                                                                       |                          \n| **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. |\n| **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.                                                    |\n| **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.                                                                                                                                                      |                               \n| **gen_descriptors**      | **false**, true            | creates `descriptor` information for integrating with reflection in existing tools                                                                                                                                                                                                                       \n\n## Reading and writing messages\n\nWe 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\u003c!--(`java_package`, `java_outer_classname`, `java_multiple_files`, `java_generate_equals_and_hash`)--\u003e.\n\n```protobuf\n// .proto definition\nmessage RootMessage {\n  optional string text = 1;\n  optional NestedMessage nested_message = 2;\n  repeated Person people_list = 3;\n}\n\nmessage NestedMessage {\n  optional double value = 1;\n}\n\nmessage Person {\n  optional uint32 id = 1;\n  optional string name = 2;\n}\n```\n\nThe 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.\n\n```Java\n// Use fluent-style to set values\nRootMessage msg = RootMessage.newInstance()\n        .setText(\"Hello World\");\n\n// Use getMutable() to set nested messages\nmsg.getMutableNestedMessage()\n        .setValue(1.0);\n\n// Write repeated values into the internally allocated list\nRepeatedMessage\u003cPerson\u003e people = msg.getMutablePeopleList().reserve(4);\nfor (int i = 0; i \u003c 4; i++) {\n    Person person = people.next()\n        .setId(i)\n        .setName(\"person \" + i);\n}\n```\n\nMessages 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.\n\n```Java\n// Convenience wrappers\nbyte[] buffer = msg.toByteArray();\nRootMessage result = RootMessage.parseFrom(buffer);\nassertEquals(result, msg);\n```\n\nThe 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.\n\n```Java\n // Reusable objects\nbyte[] buffer = new byte[512];\nProtoSink sink = ProtoSink.newArraySink();\nProtoSource source = ProtoSource.newArraySource();\n\n// Stream messages\nfor (int i = 0; i \u003c 100; i++) {\n    int length = msg.getSerializedSize();\n    msg.writeTo(sink.setOutput(buffer, 0, length));\n    result.clearQuick().mergeFrom(source.setInput(buffer, 0, length));\n}\n```\n\nAdditionally, there are also (non-optimized) convenience wrappers for `InputStream`, `OutputStream`, and `ByteBuffer`.\n\n```Java\nProtoSink.newInstance(new ByteArrayOutputStream());\nProtoSource.newInstance(new ByteArrayInputStream(bytes));\n```\n\nKeep 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.\n\n**Direct Source/Sink**\n\nDepending 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.\n\n```Java\n// Write to direct buffer\nByteBuffer directBuffer = ByteBuffer.allocateDirect(msg.getSerializedSize());\nProtoSink directSink = ProtoSink.newDirectSink();\nmsg.writeTo(directSink.setOutput(directBuffer));\ndirectBuffer.limit(directSink.getTotalBytesWritten());\n\n// Read from direct buffer\nProtoSource directSource = ProtoSource.newDirectSource();\nRootMessage result = RootMessage.parseFrom(directSource.setInput(directBuffer));\nassertEquals(msg, result);\n```\n\n**JSON Source/Sink**\n\nProtoMessages also support reading from and writing to JSON as specified in the [proto3 mapping](https://developers.google.com/protocol-buffers/docs/proto3#json).\n\n```Java\n// Set some contents\nRootMessage msg = RootMessage.newInstance();\nmsg.setText(\"👍 QuickBuffers \\uD83D\\uDC4D\");\nmsg.getMutablePeopleList().next()\n    .setId(0)\n    .setName(\"First Name\");\nmsg.getMutablePeopleList().next()\n    .setId(1)\n    .setName(\"Last Name\");\n\n// Print as prettified json\nSystem.out.println(msg);\n```\n\nThe default toString method for all messages returns prettified json. The above prints:\n\n```text\n{\n  \"text\": \"👍 QuickBuffers 👍\",\n  \"peopleList\": [{\n      \"id\": 0,\n      \"name\": \"First Name\"\n    }, {\n      \"id\": 1,\n      \"name\": \"Last Name\"\n    }]\n}\n```\n\nMore fine grained control is exposed via the `JsonSink` and `JsonSource` interfaces.\n\n```Java\n// json options\nJsonSink sink = JsonSink.newInstance()\n    .setPrettyPrinting(false)\n    .setWriteEnumsAsInts(false)\n    .setPreserveProtoFieldNames(false);\n\n// use ProtoMessage::writeTo or JsonSink::writeMessage to serialize the contents\nmsg.writeTo(sink.clear());\nRepeatedByte bytes = sink.getBytes();\n\n// use ProtoMessage::parseFrom or JsonSource::parseMessage to parse the contents\nJsonMessage result = JsonSource.newInstance(bytes)\n    .setIgnoreUnknownFields(true)\n    .parseMessage(JsonMessage.getFactory());\n```\n\nParts can be combined to convert an incoming protobuf stream to outgoing json and vice-versa\n\n```java\nmsg.clearQuick()\n    .mergeFrom(protoSource.setInput(input))\n    .writeTo(jsonSink.clear());\n```\n\nThe default implementation encodes the minimal representation accepted by the protobuf spec, i.e., floating point numbers do not append a trailing zero, and long integers are encoded without quotes. Alternative implementations based on GSON and Jackson can be found in the `quickbuf-compat` artifact.\n\nNote that the built-in JsonSink has been optimized quite a bit, but the JsonSource is very barebones due to a lack of an internal use case for JSON decoding.\n\n## Building from source\n\nThe project can be built with `mvn package` using jdk 8 through jdk 20.\n\n`mvn clean package --projects protoc-gen-quickbuf,quickbuf-runtime -am` omits building the benchmarks.\n\nNote that the `package` goal is always required, and that `mvn clean test` is not enough to work. This limitation is introduced by the plugin mechanism of `protoc`, which exchanges information with plugins via protobuf messages on `std::in` and `std::out`. Using `std::in` makes it comparatively easy to get schema information, but it is quite difficult to set up unit tests and debug plugins during development. To enable standard tests, the `protoc-gen-request` module contains a tiny protoc-plugin that stores the raw request from `std::in` inside a file that can be loaded during testing and development of the actual generator plugin. This makes the `protoc-gen-quickbuf` module dependent on the packaged output of the `protoc-gen-request` module.\n\n## Detailed accessors for different types\n\nAll nested object types such as message or repeated fields have `getField()` and `getMutableField()` accessors. Both return the same internal storage object, but `getField()` should be considered read-only. Once a field is cleared, it should also no longer be modified.\n\n### Primitive fields\n\nAll primitive values generate the same accessors and behavior as Protobuf-Java's `Builder` classes\n\n```proto\n// .proto\nmessage SimpleMessage {\n    optional int32 primitive_value = 1;\n}\n```\n\n```Java\n// simplified generated code\npublic final class SimpleMessage {\n    public SimpleMessage setPrimitiveValue(int value);\n    public SimpleMessage clearPrimitiveValue();\n    public boolean hasPrimitiveValue();\n    public int getPrimitiveValue();\n\n    private int primitiveValue;\n}\n```\n\n### Message fields\n\nNested message types are allocated internally. The recommended way to set nested message content is by accessing the internal store with `getMutableNestedMessage()`. Setting content using `setNestedMessage(NestedMessage.newInstance())` copies the data, but does not change the internal reference.\n\n```proto\n// .proto\nmessage NestedMessage {\n    optional int32 primitive_value = 1;\n}\nmessage RootMessage {\n    optional NestedMessage nested_message = 1;\n}\n```\n\n```Java\n// simplified generated code\npublic final class RootMessage {\n    public RootMessage setNestedMessage(NestedMessage value); // copies contents to internal message\n    public RootMessage clearNestedMessage(); // clears has bit as well as the backing object\n    public boolean hasNestedMessage();\n    public NestedMessage getNestedMessage(); // internal message -\u003e treat as read-only\n    public NestedMessage getMutableNestedMessage(); // internal message -\u003e may be modified until has state is cleared\n\n    private final NestedMessage nestedMessage = NestedMessage.newInstance();\n}\n```\n\n```Java\n// (1) setting nested values via 'set' (does a data copy!)\nmsg.setNestedMessage(NestedMessage().newInstance().setPrimitiveValue(0));\n\n// (2) modify the internal store directly (recommended)\nRootMessage msg = RootMessage.newInstance();\nmsg.getMutableNestedMessage().setPrimitiveValue(0);\n```\n\n\n### String fields\n\n`String` types are internally stored as `Utf8String` that are lazily parsed and can be set with `CharSequence`. Since Java `String` objects are immutable, there are additional access methods to allow for decoding characters into a reusable `StringBuilder` instance, as well as for using a custom `Utf8Decoder` that can implement interning.\n\n```proto\n// .proto\nmessage SimpleMessage {\n    optional string optional_string = 2;\n}\n```\n\n```Java\n// simplified generated code\npublic final class SimpleMessage {\n    public SimpleMessage setOptionalString(Utf8String value);\n    public SimpleMessage setOptionalString(CharSequence value);\n    public SimpleMessage clearOptionalString(); // sets length = 0\n    public boolean hasOptionalString();\n    public String getOptionalString(); // lazily converted string\n    public Utf8String getOptionalStringBytes(); // internal representation -\u003e treat as read-only\n    public Utf8String getMutableOptionalStringBytes(); // internal representation -\u003e may be modified until has state is cleared\n\n    private final Utf8String optionalString = Utf8String.newEmptyInstance();\n}\n```\n\n```Java\n// Get characters\nSimpleMessage msg = SimpleMessage.newInstance().setOptionalString(\"my-text\");\n\nStringBuilder chars = new StringBuilder();\nmsg.getOptionalStringBytes().getChars(chars); // chars now contains \"my-text\"\n```\n\n### Repeated fields\n\nRepeated scalar fields work mostly the same as String fields, but the internal `array()` can be accessed directly if needed. Repeated messages and object types provide a `next()` method that adds one element and provides a mutable reference to it.\n\n```proto\n// .proto\nmessage SimpleMessage {\n    repeated double repeated_double   = 42;\n}\n```\n\n```Java\n// simplified generated code\npublic final class SimpleMessage {\n    public SimpleMessage addRepeatedDouble(double value); // adds one value\n    public SimpleMessage addAllRepeatedDouble(double... values); // adds N values\n    public SimpleMessage clearRepeatedDouble(); // sets length = 0\n    public boolean hasRepeatedDouble();\n    public RepeatedDouble getRepeatedDouble(); // internal store -\u003e treat as read-only\n    public RepeatedDouble getMutableRepeatedDouble(); // internal store -\u003e may be modified \n\n    private final RepeatedDouble repeatedDouble = RepeatedDouble.newEmptyInstance();\n}\n```\n\n\u003c!-- \n## More information\n\n### Mutability\n\nOur main reason for creating this project was that all commonly available Protobuf implementations (Java, JavaLite, Wire) favor immutable messages, and that they can't be used without resulting in significant amounts of allocations. While this is not a problem for most applications, the GC pressure becomes an issue when working with complex nested messages at very high rates and with very low deadlines. Allocations can also become a performance bottleneck when iterating over large files with millions or more protobuf entries. QuickBuffers considers all message contents to be mutable and reusable. \n\n### Eager Allocation\n\nThe use cases we are targeting often care less about allocations during startup, but it is often important that there are no allocations in steady state. Thus, all object-type fields inside a message are `final` and are allocated immediately at object instantiation. This also makes it more likely that messages are allocated in a contiguous block and that the serialization can be done with a more sequential access pattern.\n\nUnfortunately, we currently have no way of knowing an appropriate initial size for repeated fields, so they are initialized empty and may grow as needed. In the future, we may add custom options to specify a default and/or maximum size.  (`TODO`)\n\nBe aware that this prevents the definition of cycles in the message definitions.\n\n--\u003e\n\n## Proguard configuration\n\nThere are no reflections, so none of the fields need to be preserved or special cased. However, Proguard may warn about missing methods when obfuscating against an older runtime. This is related to an intentional workaround, so the warnings can just be disabled by adding the line below to the `proguard.conf` file. R8 should automatically pick it up from the bundled [config file](./runtime/src/main/resources/META-INF/proguard/quickbuf-runtime.pro).\n\n```text\n-dontwarn us.hebi.quickbuf.JdkMethods\n```\n\n## Acknowledgements\n\nMany internals and large parts of the generated API are based on [Protobuf-Java](https://github.com/protocolbuffers/protobuf). The encoding of floating point numbers during JSON serialization is based on [Schubfach](https://github.com/c4f7fcce9cb06515/Schubfach/) [[Giu2020](https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN)]. Many other JSON parts were inspired by [dsl-json](https://github.com/ngs-doo/dsl-json), [jsoniter](https://jsoniter.com/), and [jsoniter-scala](https://github.com/plokhotnyuk/jsoniter-scala).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhebirobotics%2Fquickbuffers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhebirobotics%2Fquickbuffers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhebirobotics%2Fquickbuffers/lists"}