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