@@ -63,6 +63,23 @@ internal struct TextFormatEncodingVisitor: Visitor {
6363 self . options = options
6464 }
6565
66+ private func formatFieldName( lookingUp fieldNumber: Int ) -> [ UInt8 ] {
67+ var bytes = [ UInt8] ( )
68+ if let protoName = nameMap? . names ( for: fieldNumber) ? . proto {
69+ bytes. append ( contentsOf: protoName. utf8Buffer)
70+ } else if let protoName = nameResolver [ fieldNumber] {
71+ let buff = UnsafeBufferPointer ( start: protoName. utf8Start, count: protoName. utf8CodeUnitCount)
72+ bytes. append ( contentsOf: buff)
73+ } else if let extensionName = extensions ? [ fieldNumber] ? . protobufExtension. fieldName {
74+ bytes. append ( UInt8 ( ascii: " [ " ) )
75+ bytes. append ( contentsOf: extensionName. utf8)
76+ bytes. append ( UInt8 ( ascii: " ] " ) )
77+ } else {
78+ bytes. append ( contentsOf: fieldNumber. description. utf8)
79+ }
80+ return bytes
81+ }
82+
6683 private mutating func emitFieldName( lookingUp fieldNumber: Int ) {
6784 if let protoName = nameMap? . names ( for: fieldNumber) ? . proto {
6885 encoder. emitFieldName ( name: protoName. utf8Buffer)
@@ -220,15 +237,27 @@ internal struct TextFormatEncodingVisitor: Visitor {
220237 mutating func visitSingularMessageField< M: Message > ( value: M ,
221238 fieldNumber: Int ) throws {
222239 emitFieldName ( lookingUp: fieldNumber)
240+
241+ // Cache old encoder state
242+ let oldNameMap = self . nameMap
243+ let oldNameResolver = self . nameResolver
244+ let oldExtensions = self . extensions
245+ // Update encoding state for new message
246+ self . nameMap = ( M . self as? _ProtoNameProviding . Type) ? . _protobuf_nameMap
247+ self . nameResolver = [ : ]
248+ self . extensions = ( value as? ExtensibleMessage ) ? . _protobuf_extensionFieldValues
249+ // Encode submessage
223250 encoder. startMessageField ( )
224- var visitor = TextFormatEncodingVisitor ( message: value, encoder: encoder, options: options)
225251 if let any = value as? Google_Protobuf_Any {
226- any. textTraverse ( visitor: & visitor )
252+ any. textTraverse ( visitor: & self )
227253 } else {
228- try ! value. traverse ( visitor: & visitor )
254+ try ! value. traverse ( visitor: & self )
229255 }
230- encoder = visitor. encoder
231256 encoder. endMessageField ( )
257+ // Restore state
258+ self . extensions = oldExtensions
259+ self . nameResolver = oldNameResolver
260+ self . nameMap = oldNameMap
232261 }
233262
234263 // Emit the full "verbose" form of an Any. This writes the typeURL
@@ -372,18 +401,31 @@ internal struct TextFormatEncodingVisitor: Visitor {
372401 // Messages and groups
373402 mutating func visitRepeatedMessageField< M: Message > ( value: [ M ] ,
374403 fieldNumber: Int ) throws {
404+ // Look up field name against outer message encoding state
405+ let fieldName = formatFieldName ( lookingUp: fieldNumber)
406+ // Cache old encoder state
407+ let oldNameMap = self . nameMap
408+ let oldNameResolver = self . nameResolver
409+ let oldExtensions = self . extensions
410+ // Update encoding state for new message type
411+ self . nameMap = ( M . self as? _ProtoNameProviding . Type) ? . _protobuf_nameMap
412+ self . nameResolver = [ : ]
413+ self . extensions = ( value as? ExtensibleMessage ) ? . _protobuf_extensionFieldValues
414+ // Iterate and encode each message
375415 for v in value {
376- emitFieldName ( lookingUp : fieldNumber )
416+ encoder . emitFieldName ( name : fieldName )
377417 encoder. startMessageField ( )
378- var visitor = TextFormatEncodingVisitor ( message: v, encoder: encoder, options: options)
379418 if let any = v as? Google_Protobuf_Any {
380- any. textTraverse ( visitor: & visitor )
419+ any. textTraverse ( visitor: & self )
381420 } else {
382- try ! v. traverse ( visitor: & visitor )
421+ try ! v. traverse ( visitor: & self )
383422 }
384- encoder = visitor. encoder
385423 encoder. endMessageField ( )
386424 }
425+ // Restore state
426+ self . extensions = oldExtensions
427+ self . nameResolver = oldNameResolver
428+ self . nameMap = oldNameMap
387429 }
388430
389431 // Google's C++ implementation of Text format supports two formats
0 commit comments