Runtime support library for Wire-generated Protocol Buffer classes in Kotlin multiplatform applications
—
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.
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)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>
}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>
}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?>
}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?>
}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)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, ...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)Exception thrown when decoding encounters an unknown enum value.
class EnumConstantNotFoundException(
value: Int,
type: KClass<*>?
) : IllegalArgumentException {
@JvmField
val value: Int
}Install with Tessl CLI
npx tessl i tessl/maven-com-squareup-wire--wire-runtime