0
# Proto Adapters
1
2
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.
3
4
## Capabilities
5
6
### ProtoAdapter Base Class
7
8
Abstract base class that defines the interface for encoding and decoding protocol buffer values with type safety and metadata.
9
10
```kotlin { .api }
11
/**
12
* Abstract adapter for encoding and decoding protocol buffer values
13
* @param E The type this adapter handles
14
* @param fieldEncoding The wire encoding used for this type
15
* @param type The Kotlin class of the type
16
* @param typeUrl Type URL for google.protobuf.Any (null for non-messages)
17
* @param syntax Proto syntax version (proto2 or proto3)
18
* @param identity Identity value for proto3 (omitted when encoding)
19
* @param sourceFile Source .proto file path
20
*/
21
expect abstract class ProtoAdapter<E>(
22
fieldEncoding: FieldEncoding,
23
type: KClass<*>?,
24
typeUrl: String?,
25
syntax: Syntax,
26
identity: E? = null,
27
sourceFile: String? = null
28
) {
29
internal val fieldEncoding: FieldEncoding
30
val type: KClass<*>?
31
val typeUrl: String?
32
val syntax: Syntax
33
val identity: E?
34
val sourceFile: String?
35
36
/** Returns redacted form of value */
37
abstract fun redact(value: E): E
38
39
/** Size of non-null data value (excluding length prefix) */
40
abstract fun encodedSize(value: E): Int
41
42
/** Size of tag and value in wire format */
43
open fun encodedSizeWithTag(tag: Int, value: E?): Int
44
45
/** Write non-null value to writer */
46
@Throws(IOException::class)
47
abstract fun encode(writer: ProtoWriter, value: E)
48
49
/** Write non-null value to reverse writer */
50
@Throws(IOException::class)
51
open fun encode(writer: ReverseProtoWriter, value: E)
52
53
/** Write tag and value to writer */
54
@Throws(IOException::class)
55
open fun encodeWithTag(writer: ProtoWriter, tag: Int, value: E?)
56
57
/** Write tag and value to reverse writer */
58
@Throws(IOException::class)
59
open fun encodeWithTag(writer: ReverseProtoWriter, tag: Int, value: E?)
60
61
/** Encode value and write to sink */
62
@Throws(IOException::class)
63
fun encode(sink: BufferedSink, value: E)
64
65
/** Encode value as byte array */
66
fun encode(value: E): ByteArray
67
68
/** Encode value as ByteString */
69
fun encodeByteString(value: E): ByteString
70
71
/** Read non-null value from reader */
72
@Throws(IOException::class)
73
abstract fun decode(reader: ProtoReader): E
74
75
/** Read encoded message from bytes */
76
@Throws(IOException::class)
77
fun decode(bytes: ByteArray): E
78
79
/** Read encoded message from ByteString */
80
@Throws(IOException::class)
81
fun decode(bytes: ByteString): E
82
83
/** Read encoded message from source */
84
@Throws(IOException::class)
85
fun decode(source: BufferedSource): E
86
87
/** Try to decode value, append to destination if successful */
88
@Throws(IOException::class)
89
fun tryDecode(reader: ProtoReader, destination: MutableList<E>)
90
91
/** Human-readable version of value */
92
open fun toString(value: E): String
93
94
/** Create adapter with specified label */
95
internal fun withLabel(label: WireField.Label): ProtoAdapter<*>
96
97
/** Return adapter for packed repeated values */
98
fun asPacked(): ProtoAdapter<List<E>>
99
100
/** Return adapter for repeated values */
101
fun asRepeated(): ProtoAdapter<List<E>>
102
}
103
```
104
105
**Usage Examples:**
106
107
```kotlin
108
import com.squareup.wire.*
109
import okio.Buffer
110
111
// Using built-in adapters
112
val stringValue = "Hello, Wire!"
113
val encoded = ProtoAdapter.STRING.encode(stringValue)
114
val decoded = ProtoAdapter.STRING.decode(encoded)
115
116
// Working with tags
117
val buffer = Buffer()
118
val writer = ProtoWriter(buffer)
119
ProtoAdapter.STRING.encodeWithTag(writer, 1, stringValue)
120
ProtoAdapter.INT32.encodeWithTag(writer, 2, 42)
121
122
val reader = ProtoReader(buffer)
123
reader.forEachTag { tag ->
124
when (tag) {
125
1 -> {
126
val str = ProtoAdapter.STRING.decode(reader)
127
println("String field: $str")
128
}
129
2 -> {
130
val num = ProtoAdapter.INT32.decode(reader)
131
println("Number field: $num")
132
}
133
}
134
}
135
136
// Size calculation
137
val size = ProtoAdapter.STRING.encodedSize("test")
138
val sizeWithTag = ProtoAdapter.STRING.encodedSizeWithTag(1, "test")
139
140
// Repeated fields
141
val numbers = listOf(1, 2, 3, 4, 5)
142
val packedAdapter = ProtoAdapter.INT32.asPacked()
143
val encodedList = packedAdapter.encode(numbers)
144
val decodedList = packedAdapter.decode(encodedList)
145
```
146
147
### Built-in Scalar Adapters
148
149
Predefined adapters for all protocol buffer scalar types with proper wire encoding.
150
151
```kotlin { .api }
152
companion object {
153
/** Boolean adapter (varint encoding: 0/1) */
154
@JvmField val BOOL: ProtoAdapter<Boolean>
155
156
/** 32-bit signed integer adapter */
157
@JvmField val INT32: ProtoAdapter<Int>
158
159
/** 32-bit unsigned integer adapter */
160
@JvmField val UINT32: ProtoAdapter<Int>
161
162
/** 32-bit signed integer with ZigZag encoding */
163
@JvmField val SINT32: ProtoAdapter<Int>
164
165
/** 32-bit fixed-length integer */
166
@JvmField val FIXED32: ProtoAdapter<Int>
167
168
/** 32-bit signed fixed-length integer */
169
@JvmField val SFIXED32: ProtoAdapter<Int>
170
171
/** 64-bit signed integer adapter */
172
@JvmField val INT64: ProtoAdapter<Long>
173
174
/** 64-bit unsigned integer adapter */
175
@JvmField val UINT64: ProtoAdapter<Long>
176
177
/** 64-bit signed integer with ZigZag encoding */
178
@JvmField val SINT64: ProtoAdapter<Long>
179
180
/** 64-bit fixed-length integer */
181
@JvmField val FIXED64: ProtoAdapter<Long>
182
183
/** 64-bit signed fixed-length integer */
184
@JvmField val SFIXED64: ProtoAdapter<Long>
185
186
/** 32-bit floating point adapter */
187
@JvmField val FLOAT: ProtoAdapter<Float>
188
189
/** 64-bit floating point adapter */
190
@JvmField val DOUBLE: ProtoAdapter<Double>
191
192
/** Binary data adapter */
193
@JvmField val BYTES: ProtoAdapter<ByteString>
194
195
/** UTF-8 string adapter */
196
@JvmField val STRING: ProtoAdapter<String>
197
}
198
```
199
200
### Array Adapters
201
202
Specialized adapters for primitive arrays providing more efficient encoding than repeated fields.
203
204
```kotlin { .api }
205
companion object {
206
/** 32-bit integer array adapter (packed encoding) */
207
@JvmField val INT32_ARRAY: ProtoAdapter<IntArray>
208
@JvmField val UINT32_ARRAY: ProtoAdapter<IntArray>
209
@JvmField val SINT32_ARRAY: ProtoAdapter<IntArray>
210
@JvmField val FIXED32_ARRAY: ProtoAdapter<IntArray>
211
@JvmField val SFIXED32_ARRAY: ProtoAdapter<IntArray>
212
213
/** 64-bit integer array adapter (packed encoding) */
214
@JvmField val INT64_ARRAY: ProtoAdapter<LongArray>
215
@JvmField val UINT64_ARRAY: ProtoAdapter<LongArray>
216
@JvmField val SINT64_ARRAY: ProtoAdapter<LongArray>
217
@JvmField val FIXED64_ARRAY: ProtoAdapter<LongArray>
218
@JvmField val SFIXED64_ARRAY: ProtoAdapter<LongArray>
219
220
/** Floating point array adapters (packed encoding) */
221
@JvmField val FLOAT_ARRAY: ProtoAdapter<FloatArray>
222
@JvmField val DOUBLE_ARRAY: ProtoAdapter<DoubleArray>
223
}
224
```
225
226
### Well-Known Type Adapters
227
228
Adapters for Google Well-Known Types with proper proto3 semantics.
229
230
```kotlin { .api }
231
companion object {
232
/** Duration adapter (google.protobuf.Duration) */
233
@JvmField val DURATION: ProtoAdapter<Duration>
234
235
/** Timestamp adapter (google.protobuf.Timestamp) */
236
@JvmField val INSTANT: ProtoAdapter<Instant>
237
238
/** Empty message adapter (google.protobuf.Empty) */
239
@JvmField val EMPTY: ProtoAdapter<Unit>
240
241
/** Struct map adapter (google.protobuf.Struct) */
242
@JvmField val STRUCT_MAP: ProtoAdapter<Map<String, *>?>
243
244
/** List value adapter (google.protobuf.ListValue) */
245
@JvmField val STRUCT_LIST: ProtoAdapter<List<*>?>
246
247
/** Null value adapter (google.protobuf.NullValue) */
248
@JvmField val STRUCT_NULL: ProtoAdapter<Nothing?>
249
250
/** Value adapter (google.protobuf.Value) */
251
@JvmField val STRUCT_VALUE: ProtoAdapter<Any?>
252
}
253
```
254
255
### Wrapper Type Adapters
256
257
Adapters for protocol buffer wrapper types that handle null values and identity omission in proto3.
258
259
```kotlin { .api }
260
companion object {
261
/** Double wrapper adapter (google.protobuf.DoubleValue) */
262
@JvmField val DOUBLE_VALUE: ProtoAdapter<Double?>
263
264
/** Float wrapper adapter (google.protobuf.FloatValue) */
265
@JvmField val FLOAT_VALUE: ProtoAdapter<Float?>
266
267
/** Int64 wrapper adapter (google.protobuf.Int64Value) */
268
@JvmField val INT64_VALUE: ProtoAdapter<Long?>
269
270
/** UInt64 wrapper adapter (google.protobuf.UInt64Value) */
271
@JvmField val UINT64_VALUE: ProtoAdapter<Long?>
272
273
/** Int32 wrapper adapter (google.protobuf.Int32Value) */
274
@JvmField val INT32_VALUE: ProtoAdapter<Int?>
275
276
/** UInt32 wrapper adapter (google.protobuf.UInt32Value) */
277
@JvmField val UINT32_VALUE: ProtoAdapter<Int?>
278
279
/** Bool wrapper adapter (google.protobuf.BoolValue) */
280
@JvmField val BOOL_VALUE: ProtoAdapter<Boolean?>
281
282
/** String wrapper adapter (google.protobuf.StringValue) */
283
@JvmField val STRING_VALUE: ProtoAdapter<String?>
284
285
/** Bytes wrapper adapter (google.protobuf.BytesValue) */
286
@JvmField val BYTES_VALUE: ProtoAdapter<ByteString?>
287
}
288
```
289
290
### Map Adapter Factory
291
292
Factory method for creating type-safe map adapters for proto map fields.
293
294
```kotlin { .api }
295
companion object {
296
/**
297
* Creates adapter for map fields
298
* @param keyAdapter Adapter for map keys
299
* @param valueAdapter Adapter for map values
300
* @return Map adapter with proper encoding
301
*/
302
@JvmStatic
303
fun <K, V> newMapAdapter(
304
keyAdapter: ProtoAdapter<K>,
305
valueAdapter: ProtoAdapter<V>
306
): ProtoAdapter<Map<K, V>>
307
}
308
```
309
310
**Usage Examples:**
311
312
```kotlin
313
// Create map adapter
314
val stringToIntMap = ProtoAdapter.newMapAdapter(
315
ProtoAdapter.STRING,
316
ProtoAdapter.INT32
317
)
318
319
val map = mapOf(
320
"one" to 1,
321
"two" to 2,
322
"three" to 3
323
)
324
325
val encoded = stringToIntMap.encode(map)
326
val decoded = stringToIntMap.decode(encoded)
327
328
// Maps are encoded as repeated entries with key=1, value=2
329
val buffer = Buffer()
330
val writer = ProtoWriter(buffer)
331
stringToIntMap.encodeWithTag(writer, 1, map)
332
```
333
334
### Repeated and Packed Adapters
335
336
Adapters for repeated fields with support for packed encoding (more efficient for primitive types).
337
338
```kotlin { .api }
339
// Convert adapter to handle repeated values
340
val stringListAdapter = ProtoAdapter.STRING.asRepeated()
341
val stringList = listOf("a", "b", "c")
342
val encodedList = stringListAdapter.encodeWithTag(writer, 1, stringList)
343
344
// Convert adapter to handle packed repeated values (primitives only)
345
val intPackedAdapter = ProtoAdapter.INT32.asPacked()
346
val intList = listOf(1, 2, 3, 4, 5)
347
val encodedPacked = intPackedAdapter.encodeWithTag(writer, 2, intList)
348
349
// Packed encoding is more efficient for repeated primitives:
350
// Regular: tag+value, tag+value, tag+value, ...
351
// Packed: tag+length, value, value, value, ...
352
```
353
354
### Custom Adapter Implementation
355
356
Creating custom adapters for user-defined types:
357
358
```kotlin
359
// Example custom adapter for a simple data class
360
data class Point(val x: Int, val y: Int)
361
362
object PointAdapter : ProtoAdapter<Point>(
363
FieldEncoding.LENGTH_DELIMITED,
364
Point::class,
365
null,
366
Syntax.PROTO_2
367
) {
368
override fun encodedSize(value: Point): Int {
369
return ProtoAdapter.INT32.encodedSizeWithTag(1, value.x) +
370
ProtoAdapter.INT32.encodedSizeWithTag(2, value.y)
371
}
372
373
override fun encode(writer: ProtoWriter, value: Point) {
374
ProtoAdapter.INT32.encodeWithTag(writer, 1, value.x)
375
ProtoAdapter.INT32.encodeWithTag(writer, 2, value.y)
376
}
377
378
override fun decode(reader: ProtoReader): Point {
379
var x = 0
380
var y = 0
381
382
reader.forEachTag { tag ->
383
when (tag) {
384
1 -> x = ProtoAdapter.INT32.decode(reader)
385
2 -> y = ProtoAdapter.INT32.decode(reader)
386
else -> reader.readUnknownField(tag)
387
}
388
}
389
390
return Point(x, y)
391
}
392
393
override fun redact(value: Point): Point = Point(0, 0)
394
}
395
396
// Usage
397
val point = Point(10, 20)
398
val encoded = PointAdapter.encode(point)
399
val decoded = PointAdapter.decode(encoded)
400
```
401
402
### EnumConstantNotFoundException
403
404
Exception thrown when decoding encounters an unknown enum value.
405
406
```kotlin { .api }
407
class EnumConstantNotFoundException(
408
value: Int,
409
type: KClass<*>?
410
) : IllegalArgumentException {
411
@JvmField
412
val value: Int
413
}
414
```
415
416
## Adapter Features
417
418
### Type Safety
419
- Generic type parameters ensure compile-time type checking
420
- No casting required when using built-in adapters
421
- KClass metadata preserves runtime type information
422
423
### Proto3 Identity Handling
424
- Identity values (0, false, "", empty collections) can be omitted in proto3
425
- Wrapper adapters handle nullable types properly
426
- Proto2 vs Proto3 semantics handled automatically
427
428
### Performance Optimization
429
- Lazy evaluation where possible
430
- Packed encoding for repeated primitive types
431
- Efficient varint and ZigZag encoding for integers
432
- Stream-based processing to minimize memory usage
433
434
### Unknown Field Preservation
435
- All adapters preserve unknown fields during decode/encode cycles
436
- Forward compatibility maintained automatically
437
- Unknown fields stored as ByteString for efficient handling