my rage from v8 is spilling over
Go to file
Ben Gordon f5362e11fd
Requesting extension id 1072
This library supports an idiomatic proto3 protobuf generator for kotlin.
The library will be open sourced by Toast Inc under the Apache2 license, and is currently used in production at Toast.
The following is the readme.md that will be released with the code by the end of Q4 2019.

Supports only the Protocol Buffers language version 3.

#### Features
- Clean data class generation
- Oneof types handled as sealed classes
- JavaDoc comments on generated code
- Deprecation option pass-through to Kotlin's `@Deprecated` annotation
- Protokt-specific options: non-null types, wrapper types, interface implementation,
and more
- Tight integration with Protobuf's Java library: compatibility with its well-known
types and usage of CodedInputStream and CodedOutputStream for best performance

#### Not yet implemented
- Kotlin native support
- Kotlin JS support
- Support for gRPC service generation
- Protobuf JSON support

See examples in [protokt-testing](https://github.com/toasttab/protokt/tree/master/protokt-testing).

### Generated Code
Generated code is placed in `<buildDir>/generated-sources/main/protokt`.

A simple example:
```proto
syntax = "proto3";

package com.protokt.sample;

message Sample {
  string sample_field = 1;
}
```

will produce:
```kotlin
/*
 * Generated by protokt. Do not modify.
 */
package com.protokt.sample

import com.toasttab.protokt.rt.*

data class Sample(
    val sampleField: String,
    val unknown: Map<Int, Unknown> = emptyMap()
) : KtMessage {
    @Suppress("UNUSED")
    constructor(
        sampleField: String = ""
    ) : this(
        sampleField,
        emptyMap()
    )

    override val messageSize by lazy { sizeof() }

    override fun serialize(serializer: KtMessageSerializer) {
        if (sampleField.isNotEmpty()) {
            serializer.write(Tag(10)).write(sampleField)
        }
        if (unknown.isNotEmpty()) {
            serializer.writeUnknown(unknown)
        }
    }

    private fun sizeof(): Int {
        var res = 0
        if (sampleField.isNotEmpty()) {
            res += sizeof(Tag(1)) + sizeof(sampleField)
        }
        res += unknown.entries.sumBy { it.value.sizeof() }
        return res
    }

    companion object Deserializer : KtDeserializer<Sample> {
        override fun deserialize(deserializer: KtMessageDeserializer): Sample {
            var sampleField = ""
            val unknown = mutableMapOf<Int, Unknown>()
            while (true) {
                when (deserializer.readTag()) {
                    0 ->
                        return Sample(
                            sampleField,
                            unknown
                        )
                    10 -> sampleField = deserializer.readString()
                    else -> {
                        val unk = deserializer.readUnknown()
                        unknown[unk.fieldNum] = unknown[unk.fieldNum].let {
                            when (it) {
                                null -> unk
                                else ->
                                    when (val v = it.value) {
                                        is ListVal ->
                                            Unknown(unk.fieldNum, ListVal(v.value + unk.value))
                                        else ->
                                            Unknown(unk.fieldNum, ListVal(listOf(v, unk.value)))
                                    }
                            }
                        }
                    }
                }
            }
        }
    }
}
```

#### Runtime Notes
##### Package
The Kotlin package of a generated file can be overridden from protobuf package with the `(protokt).package` option:
```proto
syntax = "proto3";

import "protokt.proto";

package com.example;

option (protokt).package = "com.package";
```

##### Message
Each protokt message implements the `KtMessage` interface. `KtMessage` defines the `serialize()`
method and its overloads which can serialize to a byte array, a `KtMessageSerializer`, or on the JVM,
an `OutputStream`.

Each protokt message has a companion object `Deserializer` that implements the `KtDeserializer`
interface, which provides the `deserialize()` method and its overloads to construct an
instance of the message from a byte array, a Java InputStream, or others.

In order to enjoy the full benefits of Kotlin data classes, byte arrays are wrapped in the
protokt `Bytes` class, which provides appropriate `equals()` and `hashCode()` implementations.

##### Enums
Enum fields are generated as data classes with a single integer field. Kotlin enum classes are
closed and cannot retain unknown values, and protobuf requires that unknown enum values are
preserved for reserialization. This compromise exposes a constructor taking an integer, but the
`from(value: Int)` on an enum's `Deserializer` should be preferred as it avoids instantiation
when possible.


Other notes:
- `optimize_for` is ignored.
- `repeated` fields are deserialized to Lists.
- `map` fields are deserialized to Maps.
- `oneof` fields are represented as data class subtypes of a sealed base class with a single property.

### Extensions
See examples of each option in the [protokt-options](https://github.com/toasttab/protokt/tree/master/protokt-testing/protokt-options/src/main/proto)
module. All protokt-specific options require importing `protokt.proto` in the protocol file.
#### Wrapper Types
Sometimes a field on a protobuf message corresponds to a concrete nonprimitive type. In
standard protobuf the user would be responsible for this extra transformation, but the
protokt wrapper type option allows specification of a converter that will automatically
encode and decode custom types to protobuf primitives and well-known types. Some standard
types are implemented in
[protokt-extensions](https://github.com/toasttab/protokt/tree/master/protokt-extensions/src/main/kotlin/com/toasttab/protokt/ext).

Wrap a field by invoking the `(protokt_property).wrap` option:
```proto
message DateWrapperMessage {
  int64 date = 1 [
    (protokt_property).wrap = "java.util.Date"
  ];
}
```

Converters implement the `Converter` interface:
```kotlin
interface Converter<S: Any, T: Any> {
    val wrapper: KClass<S>

    fun wrap(unwrapped: T): S

    fun unwrap(wrapped: S): T
}
```

and protokt will reference the converter's methods to wrap and unwrap from protobuf primitives:
```kotlin
object DateConverter : Converter<Date, Long> {
    override val wrapper = Date::class

    override fun wrap(unwrapped: Long) =
        Date(unwrapped)

    override fun unwrap(wrapped: Date) =
        wrapped.time
}
```

```kotlin
data class WrapperModel(
    val date: java.util.Date,
    ...
) : KtMessage {
    ...
    override fun serialize(serializer: KtMessageSerializer) {
        serializer.write(Tag(10)).write(Int64(DateConverter.unwrap(date)))
        ...
    }

    override fun deserialize(deserializer: KtMessageDeserializer): WrapperModel {
        var date = 0L

        while (true) {
            when (deserializer.readTag()) {
                0 ->
                    return WrapperModel(
                        DateConverter.wrap(date),
                        ...
                    )
                ...
            }
        }
    }
}
```

Converters can also implement the `OptimizedSizeofConverter` interface adding `sizeof()`,
which allows them to optimize the calculation of the wrapper's size rather than unwrap
the object twice. For example, a UUID is always 16 bytes:

```kotlin
object UuidConverter : OptimizedSizeofConverter<UUID, ByteArray> {
    override val wrapper = UUID::class

    private val sizeofProxy = ByteArray(16)

    override fun sizeof(wrapped: UUID) =
        sizeof(sizeofProxy)

    override fun wrap(unwrapped: ByteArray): UUID {
        require(unwrapped.size == 16) {
            "input must have size 16; had ${unwrapped.size}"
        }

        return ByteBuffer.wrap(unwrapped)
            .run { UUID(long, long) }
    }

    override fun unwrap(wrapped: UUID) =
        ByteBuffer.allocate(16)
            .putLong(wrapped.mostSignificantBits)
            .putLong(wrapped.leastSignificantBits)
            .array()
}
```

Rather than convert a UUID to a byte array both for size calculation and for serialization
(which is what a naïve implementation would do), UuidConverter always returns the size of a
constant 16-byte array.

If the wrapper type is in the same package as the generated protobuf message, then it
does not need a fully-qualified name. Custom wrapper type converters can be in the same module as
protobuf types that reference them. In order to use any wrapper type defined in
`protokt-extensions`, the module must be included as a dependency:

```groovy
dependencies {
    implementation 'com.toasttab.protokt:protokt-extensions:0.0.3'
}
```

#### Interface implementation
To avoid the need to create domain-specific objects from protobuf messages you can declare
that a protobuf message implements a custom interface with properties and default methods.

```kotlin
package com.protokt.sample

interface Model {
    val id: String
}
```

```proto
package com.protokt.sample;

message ImplementsSampleMessage {
  option (protokt_class).implements = "Model";

  string id = 1;
}
```

If the wrapper interface is in the same package as the generated protobuf message, then it
does not need a fully-qualified name. Wrapper interfaces cannot be used by protobuf messages
in the same module that defines them; the dependency must be declared with`protoktExtensions`
in `build.gradle`:

```groovy
dependencies {
    protoktExtensions project(':api-module')
}
```

#### Nonnull fields
If there is a message that has no meaning whatsoever when a particular field is missing, you
can emulate proto2's `required` key word by using the `(protokt_oneof).non_null` option:

```proto
message Sample {
}

message NonNullSampleMessage {
  Sample non_null_sample = 1 [
    (protokt_property).non_null = true
  ];
}
```

Generated code will not have a nullable type so the field can be referenced without using
Kotlin's `!!`.

Oneof fields can also be declared non-null:

```proto
message NonNullSampleMessage {
  oneof non_null_oneof {
    option (protokt_oneof).non_null = true;

    string message = 2;
  }
}
```

Note that deserialization of a message with a non-nullable field will fail if the
message being decoded does not contain an instance of the required field.

#### BytesSlice
When reading messages that contain other serialized messages as `bytes` fields, protokt can
keep a reference to the originating byte array to prevent a large copy operation on
deserialization. This can be desirable when the wrapping message is a thin metadata shim and
doesn't include much memory overhead:

```proto
message SliceModel {
  int64 version = 1;

  bytes encoded_message = 2 [
    (protokt_property).bytes_slice = true
  ];
}
```

### Usage

#### Gradle

```groovy
buildscript {
    dependencies {
        classpath "com.toasttab.protokt:protokt-gradle-plugin:0.0.3"
    }
}

apply plugin: 'com.toasttab.protokt'
```

This will automatically download and install protokt, apply the Google protobuf plugin,
and configure all the necessary boilerplate. By default it will also add `protokt-runtime`
to the api scope of the project, and `protobuf-java` to the implementation scope.

If your project is pure Kotlin you may run into the following error:

```
Execution failed for task ':compileJava'.
> error: no source files
```

To work around it, disable all `JavaCompile` tasks in the project:

```groovy
tasks.withType(JavaCompile) {
    enabled = false
}
```

or:
```groovy
compileJava.enabled = false
```

#### Command line code generation

```bash
protokt-codegen$ ./gradlew assemble [OR ./gradlew installDist]

protokt-codegen$ ./run-protokt.sh -h

protokt-codegen$ ./run-protokt.sh \
  -out=../kotlin \
  -pkg=com.toasttab.protokt.conformance \
  -file=conformance.proto \
  -cp=../build/libs/protokt-codegen-0.0.3-SNAPSHOT-all.jar \
  -plugin=../bin/protokt.sh
```

### Contribution
To enable rapid development of the code generator, the protobuf conformance tests have been 
compiled and included in the protokt-testing project. They run on Mac OS 10.14+ and Ubuntu
16.04 x86-64.

Publish the plugin to the integration repository:
```bash
protokt$ ./gradlew publishToIntegrationRepository
```

Then run the tests from `protokt-testing`:
```bash
protokt-testing$ ./gradlew protokt-conformance-tests:test
```

All integration tests can be run with:
```
protokt-testing$ ./gradlew test
```
2019-07-10 09:58:38 -04:00
.github Add Mergeable config 2019-04-09 17:33:43 -07:00
benchmarks Down integrate to GitHub 2019-05-21 16:02:13 -07:00
cmake Fix builtin_atomics check in CMakeLists.txt 2019-07-08 20:30:04 -07:00
conformance Down integrate to GitHub 2019-06-26 11:01:34 -07:00
csharp Make php message class final to avoid mocking (#6277) 2019-06-18 17:12:55 -07:00
docs Requesting extension id 1072 2019-07-10 09:58:38 -04:00
editors Improve fix for https://github.com/google/protobuf/issues/295 2017-05-23 15:27:29 +02:00
examples fix dart example readme typo 2019-07-07 09:10:41 +05:30
java Down integrate to GitHub 2019-06-26 11:01:34 -07:00
js Update version to 3.9.0 2019-06-17 09:34:50 -07:00
kokoro continuous.cfg 2019-06-27 15:19:17 -04:00
m4 Mostly fix Sun Studio configuration using SunCC compiler 2018-09-12 22:18:27 -04:00
objectivec Add Xcode 11 support to the script. 2019-06-06 15:29:59 -04:00
php Merge branch 'master' into 3.9.x 2019-06-21 20:31:00 +00:00
protoc-artifacts Update version to 3.9.0 2019-06-17 09:34:50 -07:00
python Update version to 3.9.0 2019-06-17 09:34:50 -07:00
ruby Merge branch 'master' into 3.9.x 2019-06-21 20:31:00 +00:00
src Fix unused-parameter clang warnings in arena.h and map_type_handler.h 2019-07-09 11:22:38 -07:00
third_party Revert "Add stack overflow protection for text format" 2019-02-15 10:03:39 -08:00
util/python Remove hack for building Python support with Bazel. 2016-02-25 12:52:21 -08:00
.gitignore Use parameter name comment 2019-06-26 20:39:03 +02:00
.gitmodules Include googletest as a submodule (#3993) 2018-03-26 13:54:32 -07:00
appveyor.bat use netcoreapp2.1 for testing 2019-03-12 05:16:52 -04:00
appveyor.yml Merge branch '3.6.x' into merge-3-6-x 2018-06-25 13:22:10 -07:00
autogen.sh Add cpp tests under release docker image. 2018-07-15 16:41:25 -07:00
BUILD Use proto_library in proto_lang_toolchain.blacklisted_protos 2019-07-01 14:15:32 -07:00
CHANGES.txt Update change log for 3.8.0 2019-04-29 13:01:54 -07:00
compiler_config_setting.bzl Create compiler_config_setting.bzl 2018-08-28 14:10:46 +02:00
composer.json Update PHP descriptors (#3391) 2017-08-04 16:35:22 -07:00
configure.ac Update version to 3.9.0 2019-06-17 09:34:50 -07:00
CONTRIBUTING.md Update CONTRIBUTING.md 2019-05-31 15:32:02 -07:00
CONTRIBUTORS.txt Add nano proto authors and update LICENSE file to include Android.mk. 2014-11-20 14:27:46 -08:00
generate_changelog.py Replace repo links. 2018-08-22 11:55:30 -07:00
generate_descriptor_proto.sh Down-integrate from google3. 2018-09-04 10:58:54 -07:00
global.json Bump target frameworks of C# programs from netcoreapp1.0 to netcoreapp2.1 (#5838) 2019-03-11 13:03:58 -07:00
LICENSE Cleanup LICENSE file. 2018-03-26 15:03:41 -07:00
Makefile.am Merge remote-tracking branch 'origin/3.8.x' 2019-06-21 04:01:45 +00:00
post_process_dist.sh Removed javanano from post_process_dist.sh 2018-06-05 09:17:19 -07:00
protobuf_deps.bzl comply with Bazel recommendations 2019-04-15 13:43:05 -07:00
protobuf-lite.pc.in Libs should not include @PTHREAD_CFLAGS@ 2019-02-22 11:19:38 -08:00
protobuf.bzl Treat plugins as host tools. 2019-06-12 10:00:36 -07:00
protobuf.pc.in Down integrate to GitHub 2019-06-11 16:00:16 -07:00
Protobuf.podspec Update version to 3.9.0 2019-06-17 09:34:50 -07:00
README.md Added linkage monitor badge (#6327) 2019-07-01 14:59:15 -07:00
six.BUILD Add srcs_version = "PY2AND3" in BUILD files 2015-12-03 13:16:06 -08:00
tests.sh Kokoro: run Linkage Monitor on presubmit (#6318) 2019-07-01 14:03:04 -07:00
update_file_lists.sh fix duplicate mkdir in update_file_lists.sh 2018-04-25 08:22:30 +02:00
update_version.py Update version on type.pb.h 2019-06-17 14:02:29 -07:00
WORKSPACE Bazel: Add dependency to error_prone_annotations 2019-03-08 09:38:28 -08:00

Protocol Buffers - Google's data interchange format

Copyright 2008 Google Inc.

https://developers.google.com/protocol-buffers/

Overview

Protocol Buffers (a.k.a., protobuf) are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data. You can find protobuf's documentation on the Google Developers site.

This README file contains protobuf installation instructions. To install protobuf, you need to install the protocol compiler (used to compile .proto files) and the protobuf runtime for your chosen programming language.

Protocol Compiler Installation

The protocol compiler is written in C++. If you are using C++, please follow the C++ Installation Instructions to install protoc along with the C++ runtime.

For non-C++ users, the simplest way to install the protocol compiler is to download a pre-built binary from our release page:

https://github.com/protocolbuffers/protobuf/releases

In the downloads section of each release, you can find pre-built binaries in zip packages: protoc-$VERSION-$PLATFORM.zip. It contains the protoc binary as well as a set of standard .proto files distributed along with protobuf.

If you are looking for an old version that is not available in the release page, check out the maven repo here:

https://repo1.maven.org/maven2/com/google/protobuf/protoc/

These pre-built binaries are only provided for released versions. If you want to use the github master version at HEAD, or you need to modify protobuf code, or you are using C++, it's recommended to build your own protoc binary from source.

If you would like to build protoc binary from source, see the C++ Installation Instructions.

Protobuf Runtime Installation

Protobuf supports several different programming languages. For each programming language, you can find instructions in the corresponding source directory about how to install protobuf runtime for that specific language:

Language Source Ubuntu MacOS Windows
C++ (include C++ runtime and protoc) src Build status
Build status
Build status
Build status
Build status
Build status
Java java Build status
Build status
Build status
Build status
Python python Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Objective-C objectivec Build status
Build status
Build status
Build status
C# csharp Build status Build status
Build status
JavaScript js Build status Build status
Ruby ruby Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Build status
Go golang/protobuf
PHP php Build status
Build status
Build status
Build status
Dart dart-lang/protobuf Build Status

Quick Start

The best way to learn how to use protobuf is to follow the tutorials in our developer guide:

https://developers.google.com/protocol-buffers/docs/tutorials

If you want to learn from code examples, take a look at the examples in the examples directory.

Documentation

The complete documentation for Protocol Buffers is available via the web at:

https://developers.google.com/protocol-buffers/