CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-squareup-wire--wire-runtime

Runtime support library for Wire-generated Protocol Buffer classes in Kotlin multiplatform applications

Pending
Overview
Eval results
Files

proto-adapters.mddocs/

Proto Adapters

Type-safe encoding and decoding adapters for all protocol buffer types. ProtoAdapter provides a unified interface for serializing and deserializing protobuf values, with built-in adapters for all scalar types, collections, and Google Well-Known Types.

Capabilities

ProtoAdapter Base Class

Abstract base class that defines the interface for encoding and decoding protocol buffer values with type safety and metadata.

/**
 * Abstract adapter for encoding and decoding protocol buffer values
 * @param E The type this adapter handles
 * @param fieldEncoding The wire encoding used for this type
 * @param type The Kotlin class of the type
 * @param typeUrl Type URL for google.protobuf.Any (null for non-messages)
 * @param syntax Proto syntax version (proto2 or proto3)
 * @param identity Identity value for proto3 (omitted when encoding)
 * @param sourceFile Source .proto file path
 */
expect abstract class ProtoAdapter<E>(
    fieldEncoding: FieldEncoding,
    type: KClass<*>?,
    typeUrl: String?,
    syntax: Syntax,
    identity: E? = null,
    sourceFile: String? = null
) {
    internal val fieldEncoding: FieldEncoding
    val type: KClass<*>?
    val typeUrl: String?
    val syntax: Syntax
    val identity: E?
    val sourceFile: String?
    
    /** Returns redacted form of value */
    abstract fun redact(value: E): E
    
    /** Size of non-null data value (excluding length prefix) */
    abstract fun encodedSize(value: E): Int
    
    /** Size of tag and value in wire format */
    open fun encodedSizeWithTag(tag: Int, value: E?): Int
    
    /** Write non-null value to writer */
    @Throws(IOException::class)
    abstract fun encode(writer: ProtoWriter, value: E)
    
    /** Write non-null value to reverse writer */
    @Throws(IOException::class)
    open fun encode(writer: ReverseProtoWriter, value: E)
    
    /** Write tag and value to writer */
    @Throws(IOException::class)
    open fun encodeWithTag(writer: ProtoWriter, tag: Int, value: E?)
    
    /** Write tag and value to reverse writer */
    @Throws(IOException::class)
    open fun encodeWithTag(writer: ReverseProtoWriter, tag: Int, value: E?)
    
    /** Encode value and write to sink */
    @Throws(IOException::class)
    fun encode(sink: BufferedSink, value: E)
    
    /** Encode value as byte array */
    fun encode(value: E): ByteArray
    
    /** Encode value as ByteString */
    fun encodeByteString(value: E): ByteString
    
    /** Read non-null value from reader */
    @Throws(IOException::class)
    abstract fun decode(reader: ProtoReader): E
    
    /** Read encoded message from bytes */
    @Throws(IOException::class)
    fun decode(bytes: ByteArray): E
    
    /** Read encoded message from ByteString */
    @Throws(IOException::class)
    fun decode(bytes: ByteString): E
    
    /** Read encoded message from source */
    @Throws(IOException::class)
    fun decode(source: BufferedSource): E
    
    /** Try to decode value, append to destination if successful */
    @Throws(IOException::class)
    fun tryDecode(reader: ProtoReader, destination: MutableList<E>)
    
    /** Human-readable version of value */
    open fun toString(value: E): String
    
    /** Create adapter with specified label */
    internal fun withLabel(label: WireField.Label): ProtoAdapter<*>
    
    /** Return adapter for packed repeated values */
    fun asPacked(): ProtoAdapter<List<E>>
    
    /** Return adapter for repeated values */
    fun asRepeated(): ProtoAdapter<List<E>>
}

Usage Examples:

import com.squareup.wire.*
import okio.Buffer

// Using built-in adapters
val stringValue = "Hello, Wire!"
val encoded = ProtoAdapter.STRING.encode(stringValue)
val decoded = ProtoAdapter.STRING.decode(encoded)

// Working with tags
val buffer = Buffer()
val writer = ProtoWriter(buffer)
ProtoAdapter.STRING.encodeWithTag(writer, 1, stringValue)
ProtoAdapter.INT32.encodeWithTag(writer, 2, 42)

val reader = ProtoReader(buffer)
reader.forEachTag { tag ->
    when (tag) {
        1 -> {
            val str = ProtoAdapter.STRING.decode(reader)
            println("String field: $str")
        }
        2 -> {
            val num = ProtoAdapter.INT32.decode(reader)
            println("Number field: $num")
        }
    }
}

// Size calculation
val size = ProtoAdapter.STRING.encodedSize("test")
val sizeWithTag = ProtoAdapter.STRING.encodedSizeWithTag(1, "test")

// Repeated fields
val numbers = listOf(1, 2, 3, 4, 5)
val packedAdapter = ProtoAdapter.INT32.asPacked()
val encodedList = packedAdapter.encode(numbers)
val decodedList = packedAdapter.decode(encodedList)

Built-in Scalar Adapters

Predefined adapters for all protocol buffer scalar types with proper wire encoding.

companion object {
    /** Boolean adapter (varint encoding: 0/1) */
    @JvmField val BOOL: ProtoAdapter<Boolean>
    
    /** 32-bit signed integer adapter */
    @JvmField val INT32: ProtoAdapter<Int>
    
    /** 32-bit unsigned integer adapter */
    @JvmField val UINT32: ProtoAdapter<Int>
    
    /** 32-bit signed integer with ZigZag encoding */
    @JvmField val SINT32: ProtoAdapter<Int>
    
    /** 32-bit fixed-length integer */
    @JvmField val FIXED32: ProtoAdapter<Int>
    
    /** 32-bit signed fixed-length integer */
    @JvmField val SFIXED32: ProtoAdapter<Int>
    
    /** 64-bit signed integer adapter */
    @JvmField val INT64: ProtoAdapter<Long>
    
    /** 64-bit unsigned integer adapter */
    @JvmField val UINT64: ProtoAdapter<Long>
    
    /** 64-bit signed integer with ZigZag encoding */
    @JvmField val SINT64: ProtoAdapter<Long>
    
    /** 64-bit fixed-length integer */
    @JvmField val FIXED64: ProtoAdapter<Long>
    
    /** 64-bit signed fixed-length integer */
    @JvmField val SFIXED64: ProtoAdapter<Long>
    
    /** 32-bit floating point adapter */
    @JvmField val FLOAT: ProtoAdapter<Float>
    
    /** 64-bit floating point adapter */
    @JvmField val DOUBLE: ProtoAdapter<Double>
    
    /** Binary data adapter */
    @JvmField val BYTES: ProtoAdapter<ByteString>
    
    /** UTF-8 string adapter */
    @JvmField val STRING: ProtoAdapter<String>
}

Array Adapters

Specialized adapters for primitive arrays providing more efficient encoding than repeated fields.

companion object {
    /** 32-bit integer array adapter (packed encoding) */
    @JvmField val INT32_ARRAY: ProtoAdapter<IntArray>
    @JvmField val UINT32_ARRAY: ProtoAdapter<IntArray>
    @JvmField val SINT32_ARRAY: ProtoAdapter<IntArray>
    @JvmField val FIXED32_ARRAY: ProtoAdapter<IntArray>
    @JvmField val SFIXED32_ARRAY: ProtoAdapter<IntArray>
    
    /** 64-bit integer array adapter (packed encoding) */
    @JvmField val INT64_ARRAY: ProtoAdapter<LongArray>
    @JvmField val UINT64_ARRAY: ProtoAdapter<LongArray>
    @JvmField val SINT64_ARRAY: ProtoAdapter<LongArray>
    @JvmField val FIXED64_ARRAY: ProtoAdapter<LongArray>
    @JvmField val SFIXED64_ARRAY: ProtoAdapter<LongArray>
    
    /** Floating point array adapters (packed encoding) */
    @JvmField val FLOAT_ARRAY: ProtoAdapter<FloatArray>
    @JvmField val DOUBLE_ARRAY: ProtoAdapter<DoubleArray>
}

Well-Known Type Adapters

Adapters for Google Well-Known Types with proper proto3 semantics.

companion object {
    /** Duration adapter (google.protobuf.Duration) */
    @JvmField val DURATION: ProtoAdapter<Duration>
    
    /** Timestamp adapter (google.protobuf.Timestamp) */
    @JvmField val INSTANT: ProtoAdapter<Instant>
    
    /** Empty message adapter (google.protobuf.Empty) */
    @JvmField val EMPTY: ProtoAdapter<Unit>
    
    /** Struct map adapter (google.protobuf.Struct) */
    @JvmField val STRUCT_MAP: ProtoAdapter<Map<String, *>?>
    
    /** List value adapter (google.protobuf.ListValue) */
    @JvmField val STRUCT_LIST: ProtoAdapter<List<*>?>
    
    /** Null value adapter (google.protobuf.NullValue) */
    @JvmField val STRUCT_NULL: ProtoAdapter<Nothing?>
    
    /** Value adapter (google.protobuf.Value) */
    @JvmField val STRUCT_VALUE: ProtoAdapter<Any?>
}

Wrapper Type Adapters

Adapters for protocol buffer wrapper types that handle null values and identity omission in proto3.

companion object {
    /** Double wrapper adapter (google.protobuf.DoubleValue) */
    @JvmField val DOUBLE_VALUE: ProtoAdapter<Double?>
    
    /** Float wrapper adapter (google.protobuf.FloatValue) */
    @JvmField val FLOAT_VALUE: ProtoAdapter<Float?>
    
    /** Int64 wrapper adapter (google.protobuf.Int64Value) */
    @JvmField val INT64_VALUE: ProtoAdapter<Long?>
    
    /** UInt64 wrapper adapter (google.protobuf.UInt64Value) */
    @JvmField val UINT64_VALUE: ProtoAdapter<Long?>
    
    /** Int32 wrapper adapter (google.protobuf.Int32Value) */
    @JvmField val INT32_VALUE: ProtoAdapter<Int?>
    
    /** UInt32 wrapper adapter (google.protobuf.UInt32Value) */
    @JvmField val UINT32_VALUE: ProtoAdapter<Int?>
    
    /** Bool wrapper adapter (google.protobuf.BoolValue) */
    @JvmField val BOOL_VALUE: ProtoAdapter<Boolean?>
    
    /** String wrapper adapter (google.protobuf.StringValue) */
    @JvmField val STRING_VALUE: ProtoAdapter<String?>
    
    /** Bytes wrapper adapter (google.protobuf.BytesValue) */
    @JvmField val BYTES_VALUE: ProtoAdapter<ByteString?>
}

Map Adapter Factory

Factory method for creating type-safe map adapters for proto map fields.

companion object {
    /**
     * Creates adapter for map fields
     * @param keyAdapter Adapter for map keys
     * @param valueAdapter Adapter for map values
     * @return Map adapter with proper encoding
     */
    @JvmStatic
    fun <K, V> newMapAdapter(
        keyAdapter: ProtoAdapter<K>,
        valueAdapter: ProtoAdapter<V>
    ): ProtoAdapter<Map<K, V>>
}

Usage Examples:

// Create map adapter
val stringToIntMap = ProtoAdapter.newMapAdapter(
    ProtoAdapter.STRING,
    ProtoAdapter.INT32
)

val map = mapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)

val encoded = stringToIntMap.encode(map)
val decoded = stringToIntMap.decode(encoded)

// Maps are encoded as repeated entries with key=1, value=2
val buffer = Buffer()
val writer = ProtoWriter(buffer)
stringToIntMap.encodeWithTag(writer, 1, map)

Repeated and Packed Adapters

Adapters for repeated fields with support for packed encoding (more efficient for primitive types).

// Convert adapter to handle repeated values
val stringListAdapter = ProtoAdapter.STRING.asRepeated()
val stringList = listOf("a", "b", "c")
val encodedList = stringListAdapter.encodeWithTag(writer, 1, stringList)

// Convert adapter to handle packed repeated values (primitives only)
val intPackedAdapter = ProtoAdapter.INT32.asPacked()
val intList = listOf(1, 2, 3, 4, 5)  
val encodedPacked = intPackedAdapter.encodeWithTag(writer, 2, intList)

// Packed encoding is more efficient for repeated primitives:
// Regular: tag+value, tag+value, tag+value, ...
// Packed: tag+length, value, value, value, ...

Custom Adapter Implementation

Creating custom adapters for user-defined types:

// Example custom adapter for a simple data class
data class Point(val x: Int, val y: Int)

object PointAdapter : ProtoAdapter<Point>(
    FieldEncoding.LENGTH_DELIMITED,
    Point::class,
    null,
    Syntax.PROTO_2
) {
    override fun encodedSize(value: Point): Int {
        return ProtoAdapter.INT32.encodedSizeWithTag(1, value.x) +
               ProtoAdapter.INT32.encodedSizeWithTag(2, value.y)
    }
    
    override fun encode(writer: ProtoWriter, value: Point) {
        ProtoAdapter.INT32.encodeWithTag(writer, 1, value.x)
        ProtoAdapter.INT32.encodeWithTag(writer, 2, value.y)
    }
    
    override fun decode(reader: ProtoReader): Point {
        var x = 0
        var y = 0
        
        reader.forEachTag { tag ->
            when (tag) {
                1 -> x = ProtoAdapter.INT32.decode(reader)
                2 -> y = ProtoAdapter.INT32.decode(reader)
                else -> reader.readUnknownField(tag)
            }
        }
        
        return Point(x, y)
    }
    
    override fun redact(value: Point): Point = Point(0, 0)
}

// Usage
val point = Point(10, 20)
val encoded = PointAdapter.encode(point)
val decoded = PointAdapter.decode(encoded)

EnumConstantNotFoundException

Exception thrown when decoding encounters an unknown enum value.

class EnumConstantNotFoundException(
    value: Int,
    type: KClass<*>?
) : IllegalArgumentException {
    @JvmField
    val value: Int
}

Adapter Features

Type Safety

  • Generic type parameters ensure compile-time type checking
  • No casting required when using built-in adapters
  • KClass metadata preserves runtime type information

Proto3 Identity Handling

  • Identity values (0, false, "", empty collections) can be omitted in proto3
  • Wrapper adapters handle nullable types properly
  • Proto2 vs Proto3 semantics handled automatically

Performance Optimization

  • Lazy evaluation where possible
  • Packed encoding for repeated primitive types
  • Efficient varint and ZigZag encoding for integers
  • Stream-based processing to minimize memory usage

Unknown Field Preservation

  • All adapters preserve unknown fields during decode/encode cycles
  • Forward compatibility maintained automatically
  • Unknown fields stored as ByteString for efficient handling

Install with Tessl CLI

npx tessl i tessl/maven-com-squareup-wire--wire-runtime

docs

any-message.md

enum-support.md

field-annotations.md

index.md

message-framework.md

proto-adapters.md

protobuf-io.md

time-types.md

tile.json