Coverage Report

Created: 2026-06-30 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-protobuf/Sources/SwiftProtobuf/TextFormatEncodingVisitor.swift
Line
Count
Source
1
// Sources/SwiftProtobuf/TextFormatEncodingVisitor.swift - Text format encoding support
2
//
3
// Copyright (c) 2014 - 2016 Apple Inc. and the project authors
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
//
6
// See LICENSE.txt for license information:
7
// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
8
//
9
// -----------------------------------------------------------------------------
10
///
11
/// Text format serialization engine.
12
///
13
// -----------------------------------------------------------------------------
14
15
#if canImport(FoundationEssentials)
16
import FoundationEssentials
17
#else
18
import Foundation
19
#endif
20
21
private let mapNameResolver: [Int: StaticString] = [1: "key", 2: "value"]
22
23
/// Visitor that serializes a message into protobuf text format.
24
internal struct TextFormatEncodingVisitor: Visitor {
25
26
    private var encoder: TextFormatEncoder
27
    private var nameMap: _NameMap?
28
    private var nameResolver: [Int: StaticString]
29
    private var extensions: ExtensionFieldValueSet?
30
    private let options: TextFormatEncodingOptions
31
32
    /// The protobuf text produced by the visitor.
33
15.2k
    var result: String {
34
15.2k
        encoder.stringResult
35
15.2k
    }
36
37
    /// Creates a new visitor that serializes the given message to protobuf text
38
    /// format.
39
15.2k
    init(message: any Message, options: TextFormatEncodingOptions) {
40
15.2k
        let nameMap: _NameMap?
41
15.2k
        if let nameProviding = message as? (any _ProtoNameProviding) {
42
15.2k
            nameMap = type(of: nameProviding)._protobuf_nameMap
43
15.2k
        } else {
44
0
            nameMap = nil
45
0
        }
46
15.2k
        let extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
47
15.2k
48
15.2k
        self.nameMap = nameMap
49
15.2k
        self.nameResolver = [:]
50
15.2k
        self.extensions = extensions
51
15.2k
        self.encoder = TextFormatEncoder()
52
15.2k
        self.options = options
53
15.2k
    }
54
55
    // TODO: This largely duplicates emitFieldName() below.
56
    // But, it's slower so we don't want to just have emitFieldName() use
57
    // formatFieldName().  Also, we need to measure whether the optimization
58
    // this provides to repeated fields is worth the effort; consider just
59
    // removing this and having repeated fields just re-run emitFieldName()
60
    // for each item.
61
72.5k
    private func formatFieldName(lookingUp fieldNumber: Int) -> [UInt8] {
62
72.5k
        var bytes = [UInt8]()
63
72.5k
        if let protoName = nameMap?.names(for: fieldNumber)?.proto {
64
58.8k
            bytes.append(contentsOf: protoName.utf8Buffer)
65
58.8k
        } else if let protoName = nameResolver[fieldNumber] {
66
0
            let buff = UnsafeBufferPointer(start: protoName.utf8Start, count: protoName.utf8CodeUnitCount)
67
0
            bytes.append(contentsOf: buff)
68
2.84k
        } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName {
69
2.84k
            bytes.append(UInt8(ascii: "["))
70
2.84k
            bytes.append(contentsOf: extensionName.utf8)
71
2.84k
            bytes.append(UInt8(ascii: "]"))
72
10.8k
        } else {
73
10.8k
            bytes.append(contentsOf: fieldNumber.description.utf8)
74
10.8k
        }
75
72.5k
        return bytes
76
72.5k
    }
77
78
1.55M
    private mutating func emitFieldName(lookingUp fieldNumber: Int) {
79
1.55M
        if let protoName = nameMap?.names(for: fieldNumber)?.proto {
80
1.15M
            encoder.emitFieldName(name: protoName.utf8Buffer)
81
1.15M
        } else if let protoName = nameResolver[fieldNumber] {
82
371k
            encoder.emitFieldName(name: protoName)
83
371k
        } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName {
84
7.76k
            encoder.emitExtensionFieldName(name: extensionName)
85
18.0k
        } else {
86
18.0k
            encoder.emitFieldNumber(number: fieldNumber)
87
18.0k
        }
88
1.55M
    }
89
90
194k
    mutating func visitUnknown(bytes: Data) throws {
91
194k
        if options.printUnknownFields {
92
193k
            try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> Void in
93
193k
                if let baseAddress = body.baseAddress, body.count > 0 {
94
193k
                    // All fields will be directly handled, so there is no need for
95
193k
                    // the unknown field buffering/collection (when scannings to see
96
193k
                    // if something is a message, this would be extremely wasteful).
97
193k
                    var binaryOptions = BinaryDecodingOptions()
98
193k
                    binaryOptions.discardUnknownFields = true
99
193k
                    var decoder = BinaryDecoder(
100
193k
                        forReadingFrom: baseAddress,
101
193k
                        count: body.count,
102
193k
                        options: binaryOptions
103
193k
                    )
104
193k
                    try visitUnknown(decoder: &decoder)
105
193k
                }
106
193k
            }
107
194k
        }
108
194k
    }
109
110
    /// Helper for printing out unknowns.
111
    ///
112
    /// The implementation tries to be "helpful" and if a length delimited field
113
    /// appears to be a submessage, it prints it as such. However, that opens the
114
    /// door to someone sending a message with an unknown field that is a stack
115
    /// bomb, i.e. - it causes this code to recurse, exhausting the stack and
116
    /// thus opening up an attack vector. To keep this "help", but avoid the
117
    /// attack, a limit is placed on how many times it will recurse before just
118
    /// treating the length delimited fields as bytes and not trying to decode
119
    /// them.
120
    private mutating func visitUnknown(
121
        decoder: inout BinaryDecoder,
122
        recursionBudget: Int = 10
123
2.53M
    ) throws {
124
2.53M
        // This stack serves to avoid recursion for groups within groups within
125
2.53M
        // groups..., this avoid the stack attack that the message detection
126
2.53M
        // hits. No limit is placed on this because there is no stack risk with
127
2.53M
        // recursion, and because if a limit was hit, there is no other way to
128
2.53M
        // encode the group (the message field can just print as length
129
2.53M
        // delimited, groups don't have an option like that).
130
2.53M
        var groupFieldNumberStack: [Int] = []
131
2.53M
132
62.7M
        while let tag = try decoder.getTag() {
133
62.7M
            switch tag.wireFormat {
134
62.7M
            case .varint:
135
13.3M
                encoder.emitFieldNumber(number: tag.fieldNumber)
136
13.3M
                var value: UInt64 = 0
137
13.3M
                encoder.startRegularField()
138
13.3M
                try decoder.decodeSingularUInt64Field(value: &value)
139
13.3M
                encoder.putUInt64(value: value)
140
13.3M
                encoder.endRegularField()
141
62.7M
            case .fixed64:
142
515k
                encoder.emitFieldNumber(number: tag.fieldNumber)
143
515k
                var value: UInt64 = 0
144
515k
                encoder.startRegularField()
145
515k
                try decoder.decodeSingularFixed64Field(value: &value)
146
515k
                encoder.putUInt64Hex(value: value, digits: 16)
147
515k
                encoder.endRegularField()
148
62.7M
            case .lengthDelimited:
149
3.75M
                encoder.emitFieldNumber(number: tag.fieldNumber)
150
3.75M
                var bytes = Data()
151
3.75M
                try decoder.decodeSingularBytesField(value: &bytes)
152
3.75M
                var encodeAsBytes = true
153
3.75M
                if bytes.count > 0 && recursionBudget > 0 {
154
2.38M
                    bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> Void in
155
2.38M
                        if let baseAddress = body.baseAddress, body.count > 0 {
156
2.38M
                            do {
157
2.38M
                                // Walk all the fields to test if it looks like a message
158
2.38M
                                var testDecoder = BinaryDecoder(
159
2.38M
                                    forReadingFrom: baseAddress,
160
2.38M
                                    count: body.count,
161
2.38M
                                    parent: decoder
162
2.38M
                                )
163
4.75M
                                while let _ = try testDecoder.nextFieldNumber() {
164
4.75M
                                }
165
2.34M
                                // No error?  Output the message body.
166
2.34M
                                encodeAsBytes = false
167
2.34M
                                var subDecoder = BinaryDecoder(
168
2.34M
                                    forReadingFrom: baseAddress,
169
2.34M
                                    count: bytes.count,
170
2.34M
                                    parent: decoder
171
2.34M
                                )
172
2.34M
                                encoder.startMessageField()
173
2.34M
                                try visitUnknown(
174
2.34M
                                    decoder: &subDecoder,
175
2.34M
                                    recursionBudget: recursionBudget - 1
176
2.34M
                                )
177
2.34M
                                encoder.endMessageField()
178
2.34M
                            } catch {
179
38.3k
                                encodeAsBytes = true
180
2.38M
                            }
181
2.38M
                        }
182
2.38M
                    }
183
2.38M
                }
184
3.75M
                if encodeAsBytes {
185
1.41M
                    encoder.startRegularField()
186
1.41M
                    encoder.putBytesValue(value: bytes)
187
1.41M
                    encoder.endRegularField()
188
1.41M
                }
189
62.7M
            case .startGroup:
190
22.2M
                encoder.emitFieldNumber(number: tag.fieldNumber)
191
22.2M
                encoder.startMessageField()
192
22.2M
                groupFieldNumberStack.append(tag.fieldNumber)
193
62.7M
            case .endGroup:
194
22.2M
                let groupFieldNumber = groupFieldNumberStack.popLast()
195
22.2M
                // Unknown data is scanned and verified by the
196
22.2M
                // binary parser, so this can never fail.
197
22.2M
                assert(tag.fieldNumber == groupFieldNumber)
198
22.2M
                encoder.endMessageField()
199
62.7M
            case .fixed32:
200
708k
                encoder.emitFieldNumber(number: tag.fieldNumber)
201
708k
                var value: UInt32 = 0
202
708k
                encoder.startRegularField()
203
708k
                try decoder.decodeSingularFixed32Field(value: &value)
204
708k
                encoder.putUInt64Hex(value: UInt64(value), digits: 8)
205
708k
                encoder.endRegularField()
206
62.7M
            }
207
62.7M
        }
208
2.53M
209
2.53M
        // Unknown data is scanned and verified by the binary parser, so this can
210
2.53M
        // never fail.
211
2.53M
        assert(groupFieldNumberStack.isEmpty)
212
2.53M
    }
213
214
    // Visitor.swift defines default versions for other singular field types
215
    // that simply widen and dispatch to one of the following.  Since Text format
216
    // does not distinguish e.g., Fixed64 vs. UInt64, this is sufficient.
217
218
17.9k
    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
219
17.9k
        emitFieldName(lookingUp: fieldNumber)
220
17.9k
        encoder.startRegularField()
221
17.9k
        encoder.putFloatValue(value: value)
222
17.9k
        encoder.endRegularField()
223
17.9k
    }
224
225
237k
    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
226
237k
        emitFieldName(lookingUp: fieldNumber)
227
237k
        encoder.startRegularField()
228
237k
        encoder.putDoubleValue(value: value)
229
237k
        encoder.endRegularField()
230
237k
    }
231
232
977k
    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
233
977k
        emitFieldName(lookingUp: fieldNumber)
234
977k
        encoder.startRegularField()
235
977k
        encoder.putInt64(value: value)
236
977k
        encoder.endRegularField()
237
977k
    }
238
239
403k
    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
240
403k
        emitFieldName(lookingUp: fieldNumber)
241
403k
        encoder.startRegularField()
242
403k
        encoder.putUInt64(value: value)
243
403k
        encoder.endRegularField()
244
403k
    }
245
246
9.09k
    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
247
9.09k
        emitFieldName(lookingUp: fieldNumber)
248
9.09k
        encoder.startRegularField()
249
9.09k
        encoder.putBoolValue(value: value)
250
9.09k
        encoder.endRegularField()
251
9.09k
    }
252
253
509k
    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
254
509k
        emitFieldName(lookingUp: fieldNumber)
255
509k
        encoder.startRegularField()
256
509k
        encoder.putStringValue(value: value)
257
509k
        encoder.endRegularField()
258
509k
    }
259
260
399k
    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
261
399k
        emitFieldName(lookingUp: fieldNumber)
262
399k
        encoder.startRegularField()
263
399k
        encoder.putBytesValue(value: value)
264
399k
        encoder.endRegularField()
265
399k
    }
266
267
51.3k
    mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
268
51.3k
        emitFieldName(lookingUp: fieldNumber)
269
51.3k
        encoder.startRegularField()
270
51.3k
        encoder.putEnumValue(value: value)
271
51.3k
        encoder.endRegularField()
272
51.3k
    }
273
274
    mutating func visitSingularMessageField<M: Message>(
275
        value: M,
276
        fieldNumber: Int
277
390k
    ) throws {
278
390k
        emitFieldName(lookingUp: fieldNumber)
279
390k
280
390k
        // Cache old visitor configuration
281
390k
        let oldNameMap = self.nameMap
282
390k
        let oldNameResolver = self.nameResolver
283
390k
        let oldExtensions = self.extensions
284
390k
        // Update configuration for new message
285
390k
        self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap
286
390k
        self.nameResolver = [:]
287
390k
        self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
288
390k
        // Encode submessage
289
390k
        encoder.startMessageField()
290
390k
        if let any = value as? Google_Protobuf_Any {
291
320k
            any.textTraverse(visitor: &self)
292
320k
        } else {
293
70.2k
            try! value.traverse(visitor: &self)
294
390k
        }
295
390k
        encoder.endMessageField()
296
390k
        // Restore configuration before returning
297
390k
        self.extensions = oldExtensions
298
390k
        self.nameResolver = oldNameResolver
299
390k
        self.nameMap = oldNameMap
300
390k
    }
301
302
    // Emit the full "verbose" form of an Any.  This writes the typeURL
303
    // as a field name in `[...]` followed by the fields of the
304
    // contained message.
305
194k
    internal mutating func visitAnyVerbose(value: any Message, typeURL: String) {
306
194k
        encoder.emitExtensionFieldName(name: typeURL)
307
194k
        encoder.startMessageField()
308
194k
309
194k
        // Cache old visitor configuration
310
194k
        let oldNameMap = self.nameMap
311
194k
        let oldNameResolver = self.nameResolver
312
194k
        let oldExtensions = self.extensions
313
194k
        // Update configuration for new message
314
194k
        self.nameMap = (type(of: value) as? any _ProtoNameProviding.Type)?._protobuf_nameMap
315
194k
        self.nameResolver = [:]
316
194k
        self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
317
194k
318
194k
        if let any = value as? Google_Protobuf_Any {
319
4.03k
            any.textTraverse(visitor: &self)
320
190k
        } else {
321
190k
            try! value.traverse(visitor: &self)
322
194k
        }
323
194k
324
194k
        // Restore configuration before returning
325
194k
        self.extensions = oldExtensions
326
194k
        self.nameResolver = oldNameResolver
327
194k
        self.nameMap = oldNameMap
328
194k
329
194k
        encoder.endMessageField()
330
194k
    }
331
332
    // Write a single special field called "#json".  This
333
    // is used for Any objects with undecoded JSON contents.
334
0
    internal mutating func visitAnyJSONBytesField(value: Data) {
335
0
        encoder.indent()
336
0
        encoder.append(staticText: "#json: ")
337
0
        encoder.putBytesValue(value: value)
338
0
        encoder.append(staticText: "\n")
339
0
    }
340
341
    // The default implementations in Visitor.swift provide the correct
342
    // results, but we get significantly better performance by only doing
343
    // the name lookup once for the array, rather than once for each element:
344
345
2.05k
    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
346
2.05k
        assert(!value.isEmpty)
347
2.05k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
348
16.7k
        for v in value {
349
16.7k
            encoder.emitFieldName(name: fieldName)
350
16.7k
            encoder.startRegularField()
351
16.7k
            encoder.putFloatValue(value: v)
352
16.7k
            encoder.endRegularField()
353
16.7k
        }
354
2.05k
    }
355
356
1.93k
    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
357
1.93k
        assert(!value.isEmpty)
358
1.93k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
359
4.00k
        for v in value {
360
4.00k
            encoder.emitFieldName(name: fieldName)
361
4.00k
            encoder.startRegularField()
362
4.00k
            encoder.putDoubleValue(value: v)
363
4.00k
            encoder.endRegularField()
364
4.00k
        }
365
1.93k
    }
366
367
5.98k
    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
368
5.98k
        assert(!value.isEmpty)
369
5.98k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
370
8.61k
        for v in value {
371
8.61k
            encoder.emitFieldName(name: fieldName)
372
8.61k
            encoder.startRegularField()
373
8.61k
            encoder.putInt64(value: Int64(v))
374
8.61k
            encoder.endRegularField()
375
8.61k
        }
376
5.98k
    }
377
378
8.95k
    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
379
8.95k
        assert(!value.isEmpty)
380
8.95k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
381
32.2k
        for v in value {
382
32.2k
            encoder.emitFieldName(name: fieldName)
383
32.2k
            encoder.startRegularField()
384
32.2k
            encoder.putInt64(value: v)
385
32.2k
            encoder.endRegularField()
386
32.2k
        }
387
8.95k
    }
388
389
3.75k
    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
390
3.75k
        assert(!value.isEmpty)
391
3.75k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
392
103k
        for v in value {
393
103k
            encoder.emitFieldName(name: fieldName)
394
103k
            encoder.startRegularField()
395
103k
            encoder.putUInt64(value: UInt64(v))
396
103k
            encoder.endRegularField()
397
103k
        }
398
3.75k
    }
399
400
2.28k
    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
401
2.28k
        assert(!value.isEmpty)
402
2.28k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
403
3.89k
        for v in value {
404
3.89k
            encoder.emitFieldName(name: fieldName)
405
3.89k
            encoder.startRegularField()
406
3.89k
            encoder.putUInt64(value: v)
407
3.89k
            encoder.endRegularField()
408
3.89k
        }
409
2.28k
    }
410
411
1.05k
    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
412
1.05k
        try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
413
1.05k
    }
414
797
    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
415
797
        try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
416
797
    }
417
3.20k
    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
418
3.20k
        try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber)
419
3.20k
    }
420
1.03k
    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
421
1.03k
        try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber)
422
1.03k
    }
423
1.82k
    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
424
1.82k
        try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
425
1.82k
    }
426
2.52k
    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
427
2.52k
        try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
428
2.52k
    }
429
430
7.41k
    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
431
7.41k
        assert(!value.isEmpty)
432
7.41k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
433
29.0k
        for v in value {
434
29.0k
            encoder.emitFieldName(name: fieldName)
435
29.0k
            encoder.startRegularField()
436
29.0k
            encoder.putBoolValue(value: v)
437
29.0k
            encoder.endRegularField()
438
29.0k
        }
439
7.41k
    }
440
441
1.26k
    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
442
1.26k
        assert(!value.isEmpty)
443
1.26k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
444
10.5k
        for v in value {
445
10.5k
            encoder.emitFieldName(name: fieldName)
446
10.5k
            encoder.startRegularField()
447
10.5k
            encoder.putStringValue(value: v)
448
10.5k
            encoder.endRegularField()
449
10.5k
        }
450
1.26k
    }
451
452
1.88k
    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
453
1.88k
        assert(!value.isEmpty)
454
1.88k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
455
463k
        for v in value {
456
463k
            encoder.emitFieldName(name: fieldName)
457
463k
            encoder.startRegularField()
458
463k
            encoder.putBytesValue(value: v)
459
463k
            encoder.endRegularField()
460
463k
        }
461
1.88k
    }
462
463
4.13k
    mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
464
4.13k
        assert(!value.isEmpty)
465
4.13k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
466
26.5k
        for v in value {
467
26.5k
            encoder.emitFieldName(name: fieldName)
468
26.5k
            encoder.startRegularField()
469
26.5k
            encoder.putEnumValue(value: v)
470
26.5k
            encoder.endRegularField()
471
26.5k
        }
472
4.13k
    }
473
474
    // Messages and groups
475
    mutating func visitRepeatedMessageField<M: Message>(
476
        value: [M],
477
        fieldNumber: Int
478
32.8k
    ) throws {
479
32.8k
        assert(!value.isEmpty)
480
32.8k
        // Look up field name against outer message encoding state
481
32.8k
        let fieldName = formatFieldName(lookingUp: fieldNumber)
482
32.8k
        // Cache old visitor state
483
32.8k
        let oldNameMap = self.nameMap
484
32.8k
        let oldNameResolver = self.nameResolver
485
32.8k
        let oldExtensions = self.extensions
486
32.8k
        // Update encoding state for new message type
487
32.8k
        self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap
488
32.8k
        self.nameResolver = [:]
489
32.8k
        self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
490
32.8k
        // Iterate and encode each message
491
577k
        for v in value {
492
577k
            encoder.emitFieldName(name: fieldName)
493
577k
            encoder.startMessageField()
494
577k
            if let any = v as? Google_Protobuf_Any {
495
0
                any.textTraverse(visitor: &self)
496
577k
            } else {
497
577k
                try! v.traverse(visitor: &self)
498
577k
            }
499
577k
            encoder.endMessageField()
500
577k
        }
501
32.8k
        // Restore state
502
32.8k
        self.extensions = oldExtensions
503
32.8k
        self.nameResolver = oldNameResolver
504
32.8k
        self.nameMap = oldNameMap
505
32.8k
    }
506
507
    // Google's C++ implementation of Text format supports two formats
508
    // for repeated numeric fields: "short" format writes the list as a
509
    // single field with values enclosed in `[...]`, "long" format
510
    // writes a separate field name/value for each item.  They provide
511
    // an option for callers to select which output version they prefer.
512
513
    // Since this distinction mirrors the difference in Protobuf Binary
514
    // between "packed" and "non-packed", I've chosen to use the short
515
    // format for packed fields and the long version for repeated
516
    // fields.  This provides a clear visual distinction between these
517
    // fields (including proto3's default use of packed) without
518
    // introducing the baggage of a separate option.
519
520
    private mutating func iterateAndEncode<T>(
521
        packedValue: [T],
522
        fieldNumber: Int,
523
        encode: (T, inout TextFormatEncoder) -> Void
524
87.0k
    ) throws {
525
87.0k
        assert(!packedValue.isEmpty)
526
87.0k
        emitFieldName(lookingUp: fieldNumber)
527
87.0k
        encoder.startRegularField()
528
87.0k
        var firstItem = true
529
87.0k
        encoder.startArray()
530
547k
        for v in packedValue {
531
547k
            if !firstItem {
532
460k
                encoder.arraySeparator()
533
460k
            }
534
547k
            encode(v, &encoder)
535
547k
            firstItem = false
536
547k
        }
537
87.0k
        encoder.endArray()
538
87.0k
        encoder.endRegularField()
539
87.0k
    }
540
541
3.92k
    mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
542
5.69k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
543
5.69k
            (v: Float, encoder: inout TextFormatEncoder) in
544
5.69k
            encoder.putFloatValue(value: v)
545
5.69k
        }
546
3.92k
    }
547
548
6.97k
    mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
549
111k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
550
111k
            (v: Double, encoder: inout TextFormatEncoder) in
551
111k
            encoder.putDoubleValue(value: v)
552
111k
        }
553
6.97k
    }
554
555
42.3k
    mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
556
77.8k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
557
77.8k
            (v: Int32, encoder: inout TextFormatEncoder) in
558
77.8k
            encoder.putInt64(value: Int64(v))
559
77.8k
        }
560
42.3k
    }
561
562
9.15k
    mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
563
27.7k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
564
27.7k
            (v: Int64, encoder: inout TextFormatEncoder) in
565
27.7k
            encoder.putInt64(value: v)
566
27.7k
        }
567
9.15k
    }
568
569
11.2k
    mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
570
225k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
571
225k
            (v: UInt32, encoder: inout TextFormatEncoder) in
572
225k
            encoder.putUInt64(value: UInt64(v))
573
225k
        }
574
11.2k
    }
575
576
3.93k
    mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
577
19.3k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
578
19.3k
            (v: UInt64, encoder: inout TextFormatEncoder) in
579
19.3k
            encoder.putUInt64(value: v)
580
19.3k
        }
581
3.93k
    }
582
583
35.7k
    mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
584
35.7k
        try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
585
35.7k
    }
586
587
2.55k
    mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
588
2.55k
        try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
589
2.55k
    }
590
591
2.43k
    mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
592
2.43k
        try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber)
593
2.43k
    }
594
595
1.36k
    mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
596
1.36k
        try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber)
597
1.36k
    }
598
599
2.31k
    mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
600
2.31k
        try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
601
2.31k
    }
602
603
2.73k
    mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
604
2.73k
        try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
605
2.73k
    }
606
607
7.55k
    mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
608
77.2k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
609
77.2k
            (v: Bool, encoder: inout TextFormatEncoder) in
610
77.2k
            encoder.putBoolValue(value: v)
611
77.2k
        }
612
7.55k
    }
613
614
1.87k
    mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
615
3.19k
        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
616
3.19k
            (v: E, encoder: inout TextFormatEncoder) in
617
3.19k
            encoder.putEnumValue(value: v)
618
3.19k
        }
619
1.87k
    }
620
621
    /// Helper to encapsulate the common structure of iterating over a map
622
    /// and encoding the keys and values.
623
    private mutating func iterateAndEncode<K, V>(
624
        map: [K: V],
625
        fieldNumber: Int,
626
        isOrderedBefore: (K, K) -> Bool,
627
        encode: (inout TextFormatEncodingVisitor, K, V) throws -> Void
628
170k
    ) throws {
629
170k
        // Cache old visitor configuration
630
170k
        let oldNameMap = self.nameMap
631
170k
        let oldNameResolver = self.nameResolver
632
170k
        let oldExtensions = self.extensions
633
170k
634
185k
        for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) {
635
185k
            emitFieldName(lookingUp: fieldNumber)
636
185k
            encoder.startMessageField()
637
185k
638
185k
            // Update visitor configuration for map
639
185k
            self.nameMap = nil
640
185k
            self.nameResolver = mapNameResolver
641
185k
            self.extensions = nil
642
185k
643
185k
            try encode(&self, k, v)
644
185k
645
185k
            // Restore configuration before resuming containing message
646
185k
            self.extensions = oldExtensions
647
185k
            self.nameResolver = oldNameResolver
648
185k
            self.nameMap = oldNameMap
649
185k
650
185k
            encoder.endMessageField()
651
185k
        }
652
170k
    }
653
654
    mutating func visitMapField<KeyType, ValueType: MapValueType>(
655
        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
656
        value: _ProtobufMap<KeyType, ValueType>.BaseType,
657
        fieldNumber: Int
658
125k
    ) throws {
659
135k
        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
660
135k
            (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in
661
135k
            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
662
135k
            try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
663
135k
        }
664
125k
    }
665
666
    mutating func visitMapField<KeyType, ValueType>(
667
        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
668
        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
669
        fieldNumber: Int
670
41.8k
    ) throws where ValueType.RawValue == Int {
671
45.9k
        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
672
45.9k
            (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in
673
45.9k
            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
674
45.9k
            try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
675
45.9k
        }
676
41.8k
    }
677
678
    mutating func visitMapField<KeyType, ValueType>(
679
        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
680
        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
681
        fieldNumber: Int
682
3.43k
    ) throws {
683
4.24k
        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
684
4.24k
            (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in
685
4.24k
            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
686
4.24k
            try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
687
4.24k
        }
688
3.43k
    }
689
}