/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 | | } |