or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

any-message.mdenum-support.mdfield-annotations.mdindex.mdmessage-framework.mdproto-adapters.mdprotobuf-io.mdtime-types.md

protobuf-io.mddocs/

0

# Protocol Buffer I/O

1

2

Low-level reading and writing of protocol buffer wire format data. The ProtoReader and ProtoWriter classes provide efficient streaming I/O for all protobuf field types with proper handling of packed fields, nested messages, and unknown fields.

3

4

## Capabilities

5

6

### ProtoReader

7

8

Reads and decodes protocol message fields from a BufferedSource with support for nested messages, packed fields, and unknown field preservation.

9

10

```kotlin { .api }

11

/**

12

* Reads and decodes protocol message fields

13

* @param source The BufferedSource to read from

14

*/

15

class ProtoReader(private val source: BufferedSource) {

16

/** Begin a nested message, returns token for endMessage */

17

@Throws(IOException::class)

18

fun beginMessage(): Long

19

20

/** End nested message and return unknown fields */

21

@Throws(IOException::class)

22

fun endMessageAndGetUnknownFields(token: Long): ByteString

23

24

/** Read next tag, returns -1 if no more tags */

25

@Throws(IOException::class)

26

fun nextTag(): Int

27

28

/** Get encoding of next field value */

29

fun peekFieldEncoding(): FieldEncoding?

30

31

/** Skip current field value */

32

@Throws(IOException::class)

33

fun skip()

34

35

/** Read bytes field value */

36

@Throws(IOException::class)

37

fun readBytes(): ByteString

38

39

/** Read string field value */

40

@Throws(IOException::class)

41

fun readString(): String

42

43

/** Read 32-bit varint */

44

@Throws(IOException::class)

45

fun readVarint32(): Int

46

47

/** Read 64-bit varint */

48

@Throws(IOException::class)

49

fun readVarint64(): Long

50

51

/** Read 32-bit little-endian integer */

52

@Throws(IOException::class)

53

fun readFixed32(): Int

54

55

/** Read 64-bit little-endian integer */

56

@Throws(IOException::class)

57

fun readFixed64(): Long

58

59

/** Read length of next length-delimited message */

60

@Throws(IOException::class)

61

fun nextLengthDelimited(): Int

62

63

/** Process each tag with handler function */

64

inline fun forEachTag(tagHandler: (Int) -> Any): ByteString

65

66

/** Read unknown field and store for later retrieval */

67

fun readUnknownField(tag: Int)

68

69

/** Add already-read unknown field */

70

fun addUnknownField(tag: Int, fieldEncoding: FieldEncoding, value: Any?)

71

72

/** Get minimum length in bytes of next field */

73

fun nextFieldMinLengthInBytes(): Long

74

}

75

```

76

77

**Usage Examples:**

78

79

```kotlin

80

import com.squareup.wire.*

81

import okio.Buffer

82

import okio.ByteString

83

84

// Reading a simple message

85

val buffer = Buffer().write(encodedData)

86

val reader = ProtoReader(buffer)

87

88

// Process all fields in a message

89

val unknownFields = reader.forEachTag { tag ->

90

when (tag) {

91

1 -> {

92

val name = ProtoAdapter.STRING.decode(reader)

93

// Handle name field

94

}

95

2 -> {

96

val age = ProtoAdapter.INT32.decode(reader)

97

// Handle age field

98

}

99

else -> reader.readUnknownField(tag)

100

}

101

}

102

103

// Manual field reading

104

val reader2 = ProtoReader(buffer)

105

val token = reader2.beginMessage()

106

while (true) {

107

val tag = reader2.nextTag()

108

if (tag == -1) break

109

110

when (tag) {

111

1 -> {

112

val encoding = reader2.peekFieldEncoding()

113

if (encoding == FieldEncoding.LENGTH_DELIMITED) {

114

val value = reader2.readString()

115

}

116

}

117

2 -> {

118

val value = reader2.readVarint32()

119

}

120

else -> reader2.skip()

121

}

122

}

123

val unknownFields2 = reader2.endMessageAndGetUnknownFields(token)

124

125

// Reading nested messages

126

val reader3 = ProtoReader(buffer)

127

val outerToken = reader3.beginMessage()

128

while (true) {

129

val tag = reader3.nextTag()

130

if (tag == -1) break

131

132

if (tag == 3) { // nested message field

133

val innerToken = reader3.beginMessage()

134

// Read inner message fields...

135

reader3.endMessageAndGetUnknownFields(innerToken)

136

}

137

}

138

reader3.endMessageAndGetUnknownFields(outerToken)

139

```

140

141

### ProtoWriter

142

143

Encodes and writes protocol message fields to a BufferedSink with support for all protobuf wire types and proper tag encoding.

144

145

```kotlin { .api }

146

/**

147

* Utilities for encoding and writing protocol message fields

148

* @param sink The BufferedSink to write to

149

*/

150

class ProtoWriter(private val sink: BufferedSink) {

151

/** Write bytes value */

152

@Throws(IOException::class)

153

fun writeBytes(value: ByteString)

154

155

/** Write string value */

156

@Throws(IOException::class)

157

fun writeString(value: String)

158

159

/** Encode and write a tag */

160

@Throws(IOException::class)

161

fun writeTag(fieldNumber: Int, fieldEncoding: FieldEncoding)

162

163

/** Write signed 32-bit varint */

164

@Throws(IOException::class)

165

internal fun writeSignedVarint32(value: Int)

166

167

/** Write unsigned 32-bit varint */

168

@Throws(IOException::class)

169

fun writeVarint32(value: Int)

170

171

/** Write 64-bit varint */

172

@Throws(IOException::class)

173

fun writeVarint64(value: Long)

174

175

/** Write little-endian 32-bit integer */

176

@Throws(IOException::class)

177

fun writeFixed32(value: Int)

178

179

/** Write little-endian 64-bit integer */

180

@Throws(IOException::class)

181

fun writeFixed64(value: Long)

182

}

183

```

184

185

**Usage Examples:**

186

187

```kotlin

188

import com.squareup.wire.*

189

import okio.Buffer

190

191

// Writing a simple message

192

val buffer = Buffer()

193

val writer = ProtoWriter(buffer)

194

195

// Write string field (tag 1)

196

writer.writeTag(1, FieldEncoding.LENGTH_DELIMITED)

197

writer.writeVarint32("Alice".utf8Size().toInt())

198

writer.writeString("Alice")

199

200

// Write integer field (tag 2)

201

writer.writeTag(2, FieldEncoding.VARINT)

202

writer.writeVarint32(30)

203

204

// Write bytes field (tag 3)

205

val data = ByteString.encodeUtf8("binary data")

206

writer.writeTag(3, FieldEncoding.LENGTH_DELIMITED)

207

writer.writeVarint32(data.size)

208

writer.writeBytes(data)

209

210

// Get encoded result

211

val encoded = buffer.readByteArray()

212

213

// Using adapters for convenience (recommended approach)

214

val buffer2 = Buffer()

215

val writer2 = ProtoWriter(buffer2)

216

217

ProtoAdapter.STRING.encodeWithTag(writer2, 1, "Alice")

218

ProtoAdapter.INT32.encodeWithTag(writer2, 2, 30)

219

ProtoAdapter.BYTES.encodeWithTag(writer2, 3, data)

220

```

221

222

### ProtoWriter Companion Utilities

223

224

Static utilities for computing sizes and encoding ZigZag values.

225

226

```kotlin { .api }

227

companion object {

228

/** Make tag value given field number and wire type */

229

internal fun makeTag(fieldNumber: Int, fieldEncoding: FieldEncoding): Int

230

231

/** Compute bytes needed to encode a tag */

232

internal fun tagSize(tag: Int): Int

233

234

/** Compute bytes needed for signed 32-bit integer */

235

internal fun int32Size(value: Int): Int

236

237

/** Compute bytes needed for 32-bit varint */

238

internal fun varint32Size(value: Int): Int

239

240

/** Compute bytes needed for 64-bit varint */

241

internal fun varint64Size(value: Long): Int

242

243

/** Encode ZigZag 32-bit value for efficient negative number encoding */

244

internal fun encodeZigZag32(n: Int): Int

245

246

/** Decode ZigZag 32-bit value */

247

internal fun decodeZigZag32(n: Int): Int

248

249

/** Encode ZigZag 64-bit value for efficient negative number encoding */

250

internal fun encodeZigZag64(n: Long): Long

251

252

/** Decode ZigZag 64-bit value */

253

internal fun decodeZigZag64(n: Long): Long

254

}

255

```

256

257

### ReverseProtoWriter

258

259

Reverse protocol buffer writer for optimized encoding in certain scenarios.

260

261

```kotlin { .api }

262

/**

263

* Reverse protocol buffer writer for optimized encoding

264

*/

265

class ReverseProtoWriter {

266

/** Current byte count */

267

val byteCount: Long

268

269

/** Write to forward writer callback */

270

fun writeForward(block: (ProtoWriter) -> Unit)

271

272

/** Write to buffered sink */

273

fun writeTo(sink: BufferedSink)

274

275

// Similar methods to ProtoWriter but optimized for reverse writing

276

fun writeTag(fieldNumber: Int, fieldEncoding: FieldEncoding)

277

fun writeString(value: String)

278

fun writeBytes(value: ByteString)

279

fun writeVarint32(value: Int)

280

fun writeVarint64(value: Long)

281

fun writeFixed32(value: Int)

282

fun writeFixed64(value: Long)

283

}

284

```

285

286

## Reading Patterns

287

288

### Message Reading Pattern

289

290

Standard pattern for reading a complete protocol buffer message:

291

292

```kotlin

293

fun readMessage(source: BufferedSource): SomeMessage {

294

val reader = ProtoReader(source)

295

296

var field1: String = ""

297

var field2: Int = 0

298

var field3: List<String> = emptyList()

299

300

val unknownFields = reader.forEachTag { tag ->

301

when (tag) {

302

1 -> field1 = ProtoAdapter.STRING.decode(reader)

303

2 -> field2 = ProtoAdapter.INT32.decode(reader)

304

3 -> field3 += ProtoAdapter.STRING.decode(reader)

305

else -> reader.readUnknownField(tag)

306

}

307

}

308

309

return SomeMessage(field1, field2, field3, unknownFields)

310

}

311

```

312

313

### Packed Field Reading

314

315

Reading packed repeated fields (more efficient encoding for repeated primitive types):

316

317

```kotlin

318

// Reading packed integers

319

val reader = ProtoReader(buffer)

320

val tag = reader.nextTag()

321

if (tag == expectedTag) {

322

val values = mutableListOf<Int>()

323

val adapter = ProtoAdapter.INT32.asPacked()

324

val packedList = adapter.decode(reader)

325

values.addAll(packedList)

326

}

327

```

328

329

## Writing Patterns

330

331

### Message Writing Pattern

332

333

Standard pattern for writing a complete protocol buffer message:

334

335

```kotlin

336

fun writeMessage(message: SomeMessage, sink: BufferedSink) {

337

val writer = ProtoWriter(sink)

338

339

// Write fields in tag order

340

if (message.field1.isNotEmpty()) {

341

ProtoAdapter.STRING.encodeWithTag(writer, 1, message.field1)

342

}

343

if (message.field2 != 0) {

344

ProtoAdapter.INT32.encodeWithTag(writer, 2, message.field2)

345

}

346

for (item in message.field3) {

347

ProtoAdapter.STRING.encodeWithTag(writer, 3, item)

348

}

349

350

// Write unknown fields

351

if (message.unknownFields.size > 0) {

352

writer.writeBytes(message.unknownFields)

353

}

354

}

355

```

356

357

## Performance Considerations

358

359

- **Streaming**: Both readers and writers operate on streams for memory efficiency

360

- **Lazy Evaluation**: Unknown fields are processed lazily to avoid unnecessary work

361

- **Buffer Reuse**: Reuse Buffer instances where possible to reduce allocations

362

- **Packed Fields**: Use packed encoding for repeated primitive types to reduce wire size

363

- **ZigZag Encoding**: Negative numbers use ZigZag encoding for more efficient varint representation