Coverage Report

Created: 2026-03-26 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-protobuf/Sources/SwiftProtobuf/BinaryEncodingVisitor.swift
Line
Count
Source
1
// Sources/SwiftProtobuf/BinaryEncodingVisitor.swift - Binary 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
/// Core support for protobuf binary encoding.  Note that this is built
12
/// on the general traversal machinery.
13
///
14
// -----------------------------------------------------------------------------
15
16
#if canImport(FoundationEssentials)
17
import FoundationEssentials
18
#else
19
import Foundation
20
#endif
21
22
/// Visitor that encodes a message graph in the protobuf binary wire format.
23
internal struct BinaryEncodingVisitor: Visitor {
24
    private let options: BinaryEncodingOptions
25
26
    var encoder: BinaryEncoder
27
28
    /// Creates a new visitor that writes the binary-coded message into the memory
29
    /// at the given pointer.
30
    ///
31
    /// - Precondition: `pointer` must point to an allocated block of memory that
32
    ///   is large enough to hold the entire encoded message. For performance
33
    ///   reasons, the encoder does not make any attempts to verify this.
34
416k
    init(forWritingInto buffer: UnsafeMutableRawBufferPointer, options: BinaryEncodingOptions) {
35
416k
        self.encoder = BinaryEncoder(forWritingInto: buffer)
36
416k
        self.options = options
37
416k
    }
38
39
361k
    mutating func visitUnknown(bytes: Data) throws {
40
361k
        encoder.appendUnknown(data: bytes)
41
361k
    }
42
43
2.33M
    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
44
2.33M
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32)
45
2.33M
        encoder.putFloatValue(value: value)
46
2.33M
    }
47
48
776k
    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
49
776k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64)
50
776k
        encoder.putDoubleValue(value: value)
51
776k
    }
52
53
10.7M
    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
54
10.7M
        try visitSingularUInt64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber)
55
10.7M
    }
56
57
50.3M
    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
58
50.3M
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .varint)
59
50.3M
        encoder.putVarInt(value: value)
60
50.3M
    }
61
62
7.04M
    mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
63
7.04M
        try visitSingularSInt64Field(value: Int64(value), fieldNumber: fieldNumber)
64
7.04M
    }
65
66
12.8M
    mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
67
12.8M
        try visitSingularUInt64Field(value: ZigZag.encoded(value), fieldNumber: fieldNumber)
68
12.8M
    }
69
70
8.07M
    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
71
8.07M
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32)
72
8.07M
        encoder.putFixedUInt32(value: value)
73
8.07M
    }
74
75
3.32M
    mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
76
3.32M
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64)
77
3.32M
        encoder.putFixedUInt64(value: value)
78
3.32M
    }
79
80
4.52M
    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
81
4.52M
        try visitSingularFixed32Field(value: UInt32(bitPattern: value), fieldNumber: fieldNumber)
82
4.52M
    }
83
84
1.89M
    mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
85
1.89M
        try visitSingularFixed64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber)
86
1.89M
    }
87
88
14.0M
    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
89
14.0M
        try visitSingularUInt64Field(value: value ? 1 : 0, fieldNumber: fieldNumber)
90
14.0M
    }
91
92
861k
    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
93
861k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
94
861k
        encoder.putStringValue(value: value)
95
861k
    }
96
97
499k
    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
98
499k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
99
499k
        encoder.putBytesValue(value: value)
100
499k
    }
101
102
    mutating func visitSingularEnumField<E: Enum>(
103
        value: E,
104
        fieldNumber: Int
105
1.28M
    ) throws {
106
1.28M
        try visitSingularUInt64Field(
107
1.28M
            value: UInt64(bitPattern: Int64(value.rawValue)),
108
1.28M
            fieldNumber: fieldNumber
109
1.28M
        )
110
1.28M
    }
111
112
    mutating func visitSingularMessageField<M: Message>(
113
        value: M,
114
        fieldNumber: Int
115
2.67M
    ) throws {
116
2.67M
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
117
2.67M
        let length = try value.serializedDataSize()
118
2.67M
        encoder.putVarInt(value: length)
119
2.67M
        try value.traverse(visitor: &self)
120
2.67M
    }
121
122
89.3k
    mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
123
89.3k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .startGroup)
124
89.3k
        try value.traverse(visitor: &self)
125
89.3k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .endGroup)
126
89.3k
    }
127
128
    // Repeated fields are handled by the default implementations in Visitor.swift
129
130
    // Packed Fields
131
132
18.1k
    mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
133
18.1k
        assert(!value.isEmpty)
134
18.1k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
135
18.1k
        encoder.putVarInt(value: value.count * MemoryLayout<Float>.size)
136
538k
        for v in value {
137
538k
            encoder.putFloatValue(value: v)
138
538k
        }
139
18.1k
    }
140
141
21.6k
    mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
142
21.6k
        assert(!value.isEmpty)
143
21.6k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
144
21.6k
        encoder.putVarInt(value: value.count * MemoryLayout<Double>.size)
145
940k
        for v in value {
146
940k
            encoder.putDoubleValue(value: v)
147
940k
        }
148
21.6k
    }
149
150
13.1k
    mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
151
13.1k
        assert(!value.isEmpty)
152
13.1k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
153
3.40M
        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
154
13.1k
        encoder.putVarInt(value: packedSize)
155
3.39M
        for v in value {
156
3.39M
            encoder.putVarInt(value: Int64(v))
157
3.39M
        }
158
13.1k
    }
159
160
12.3k
    mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
161
12.3k
        assert(!value.isEmpty)
162
12.3k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
163
4.61M
        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
164
12.3k
        encoder.putVarInt(value: packedSize)
165
4.59M
        for v in value {
166
4.59M
            encoder.putVarInt(value: v)
167
4.59M
        }
168
12.3k
    }
169
170
14.6k
    mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
171
14.6k
        assert(!value.isEmpty)
172
14.6k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
173
273k
        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
174
14.6k
        encoder.putVarInt(value: packedSize)
175
268k
        for v in value {
176
268k
            encoder.putZigZagVarInt(value: Int64(v))
177
268k
        }
178
14.6k
    }
179
180
18.4k
    mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
181
18.4k
        assert(!value.isEmpty)
182
18.4k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
183
397k
        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
184
18.4k
        encoder.putVarInt(value: packedSize)
185
376k
        for v in value {
186
376k
            encoder.putZigZagVarInt(value: v)
187
376k
        }
188
18.4k
    }
189
190
18.8k
    mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
191
18.8k
        assert(!value.isEmpty)
192
18.8k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
193
816k
        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
194
18.8k
        encoder.putVarInt(value: packedSize)
195
784k
        for v in value {
196
784k
            encoder.putVarInt(value: UInt64(v))
197
784k
        }
198
18.8k
    }
199
200
26.3k
    mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
201
26.3k
        assert(!value.isEmpty)
202
26.3k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
203
2.92M
        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
204
26.3k
        encoder.putVarInt(value: packedSize)
205
2.90M
        for v in value {
206
2.90M
            encoder.putVarInt(value: v)
207
2.90M
        }
208
26.3k
    }
209
210
27.7k
    mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
211
27.7k
        assert(!value.isEmpty)
212
27.7k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
213
27.7k
        encoder.putVarInt(value: value.count * MemoryLayout<UInt32>.size)
214
168k
        for v in value {
215
168k
            encoder.putFixedUInt32(value: v)
216
168k
        }
217
27.7k
    }
218
219
39.7k
    mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
220
39.7k
        assert(!value.isEmpty)
221
39.7k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
222
39.7k
        encoder.putVarInt(value: value.count * MemoryLayout<UInt64>.size)
223
626k
        for v in value {
224
626k
            encoder.putFixedUInt64(value: v)
225
626k
        }
226
39.7k
    }
227
228
13.1k
    mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
229
13.1k
        assert(!value.isEmpty)
230
13.1k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
231
13.1k
        encoder.putVarInt(value: value.count * MemoryLayout<Int32>.size)
232
1.11M
        for v in value {
233
1.11M
            encoder.putFixedUInt32(value: UInt32(bitPattern: v))
234
1.11M
        }
235
13.1k
    }
236
237
20.1k
    mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
238
20.1k
        assert(!value.isEmpty)
239
20.1k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
240
20.1k
        encoder.putVarInt(value: value.count * MemoryLayout<Int64>.size)
241
584k
        for v in value {
242
584k
            encoder.putFixedUInt64(value: UInt64(bitPattern: v))
243
584k
        }
244
20.1k
    }
245
246
16.6k
    mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
247
16.6k
        assert(!value.isEmpty)
248
16.6k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
249
16.6k
        encoder.putVarInt(value: value.count)
250
1.59M
        for v in value {
251
1.59M
            encoder.putVarInt(value: v ? 1 : 0)
252
1.59M
        }
253
16.6k
    }
254
255
40.2k
    mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
256
40.2k
        assert(!value.isEmpty)
257
40.2k
        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
258
305k
        let packedSize = value.reduce(0) {
259
305k
            $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
260
305k
        }
261
40.2k
        encoder.putVarInt(value: packedSize)
262
284k
        for v in value {
263
284k
            encoder.putVarInt(value: v.rawValue)
264
284k
        }
265
40.2k
    }
266
267
    mutating func visitMapField<KeyType, ValueType: MapValueType>(
268
        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
269
        value: _ProtobufMap<KeyType, ValueType>.BaseType,
270
        fieldNumber: Int
271
550k
    ) throws {
272
550k
        try iterateAndEncode(
273
550k
            map: value,
274
550k
            fieldNumber: fieldNumber,
275
550k
            isOrderedBefore: KeyType._lessThan,
276
969k
            encodeWithSizer: { sizer, key, value in
277
969k
                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
278
969k
                try ValueType.visitSingular(value: value, fieldNumber: 2, with: &sizer)
279
969k
            },
280
969k
            encodeWithVisitor: { visitor, key, value in
281
969k
                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
282
969k
                try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
283
969k
            }
284
550k
        )
285
550k
    }
286
287
    mutating func visitMapField<KeyType, ValueType>(
288
        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
289
        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
290
        fieldNumber: Int
291
59.1k
    ) throws where ValueType.RawValue == Int {
292
59.1k
        try iterateAndEncode(
293
59.1k
            map: value,
294
59.1k
            fieldNumber: fieldNumber,
295
59.1k
            isOrderedBefore: KeyType._lessThan,
296
206k
            encodeWithSizer: { sizer, key, value in
297
206k
                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
298
206k
                try sizer.visitSingularEnumField(value: value, fieldNumber: 2)
299
206k
            },
300
206k
            encodeWithVisitor: { visitor, key, value in
301
206k
                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
302
206k
                try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
303
206k
            }
304
59.1k
        )
305
59.1k
    }
306
307
    mutating func visitMapField<KeyType, ValueType>(
308
        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
309
        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
310
        fieldNumber: Int
311
76.4k
    ) throws {
312
76.4k
        try iterateAndEncode(
313
76.4k
            map: value,
314
76.4k
            fieldNumber: fieldNumber,
315
76.4k
            isOrderedBefore: KeyType._lessThan,
316
600k
            encodeWithSizer: { sizer, key, value in
317
600k
                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
318
600k
                try sizer.visitSingularMessageField(value: value, fieldNumber: 2)
319
600k
            },
320
600k
            encodeWithVisitor: { visitor, key, value in
321
600k
                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
322
600k
                try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
323
600k
            }
324
76.4k
        )
325
76.4k
    }
326
327
    /// Helper to encapsulate the common structure of iterating over a map
328
    /// and encoding the keys and values.
329
    private mutating func iterateAndEncode<K, V>(
330
        map: [K: V],
331
        fieldNumber: Int,
332
        isOrderedBefore: (K, K) -> Bool,
333
        encodeWithSizer: (inout BinaryEncodingSizeVisitor, K, V) throws -> Void,
334
        encodeWithVisitor: (inout BinaryEncodingVisitor, K, V) throws -> Void
335
1.04M
    ) throws {
336
1.04M
        if options.useDeterministicOrdering {
337
0
            for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) {
338
0
                encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
339
0
                var sizer = BinaryEncodingSizeVisitor()
340
0
                try encodeWithSizer(&sizer, k, v)
341
0
                let entrySize = sizer.serializedSize
342
0
                encoder.putVarInt(value: entrySize)
343
0
                try encodeWithVisitor(&self, k, v)
344
0
            }
345
1.04M
        } else {
346
1.77M
            for (k, v) in map {
347
1.77M
                encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
348
1.77M
                var sizer = BinaryEncodingSizeVisitor()
349
1.77M
                try encodeWithSizer(&sizer, k, v)
350
1.77M
                let entrySize = sizer.serializedSize
351
1.77M
                encoder.putVarInt(value: entrySize)
352
1.77M
                try encodeWithVisitor(&self, k, v)
353
1.77M
            }
354
1.04M
        }
355
1.04M
    }
356
357
    mutating func visitExtensionFieldsAsMessageSet(
358
        fields: ExtensionFieldValueSet,
359
        start: Int,
360
        end: Int
361
661k
    ) throws {
362
661k
        var subVisitor = BinaryEncodingMessageSetVisitor(encoder: encoder, options: options)
363
661k
        try fields.traverse(visitor: &subVisitor, start: start, end: end)
364
661k
        encoder = subVisitor.encoder
365
661k
    }
366
}
367
368
extension BinaryEncodingVisitor {
369
370
    // Helper Visitor to when writing out the extensions as MessageSets.
371
    internal struct BinaryEncodingMessageSetVisitor: SelectiveVisitor {
372
        private let options: BinaryEncodingOptions
373
374
        var encoder: BinaryEncoder
375
376
661k
        init(encoder: BinaryEncoder, options: BinaryEncodingOptions) {
377
661k
            self.options = options
378
661k
            self.encoder = encoder
379
661k
        }
380
381
4.50k
        mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
382
4.50k
            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemStart.rawValue))
383
4.50k
384
4.50k
            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.typeId.rawValue))
385
4.50k
            encoder.putVarInt(value: fieldNumber)
386
4.50k
387
4.50k
            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.message.rawValue))
388
4.50k
389
4.50k
            // Use a normal BinaryEncodingVisitor so any message fields end up in the
390
4.50k
            // normal wire format (instead of MessageSet format).
391
4.50k
            let length = try value.serializedDataSize()
392
4.50k
            encoder.putVarInt(value: length)
393
4.50k
            // Create the sub encoder after writing the length.
394
4.50k
            var subVisitor = BinaryEncodingVisitor(
395
4.50k
                forWritingInto: encoder.remainder,
396
4.50k
                options: options
397
4.50k
            )
398
4.50k
            try value.traverse(visitor: &subVisitor)
399
4.50k
            encoder.advance(subVisitor.encoder.used)
400
4.50k
401
4.50k
            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemEnd.rawValue))
402
4.50k
        }
403
404
        // SelectiveVisitor handles the rest.
405
    }
406
}