Forráskód Böngészése

Run `swift-format -p -r -i .`

Thomas Van Lenten 1 éve
szülő
commit
ac8da77c93
100 módosított fájl, 13061 hozzáadás és 12650 törlés
  1. 5 5
      CompileTests/InternalImportsByDefault/Package.swift
  2. 1 1
      CompileTests/MultiModule/Package.swift
  3. 23 23
      CompileTests/MultiModule/Tests/Test1/test1.swift
  4. 23 23
      CompileTests/MultiModule/Tests/Test2/test2.swift
  5. 16 9
      FuzzTesting/Package.swift
  6. 33 34
      FuzzTesting/Sources/FuzzAsyncMessageSequence/main.swift
  7. 17 17
      FuzzTesting/Sources/FuzzBinary/main.swift
  8. 29 30
      FuzzTesting/Sources/FuzzBinaryDelimited/main.swift
  9. 3 4
      FuzzTesting/Sources/FuzzCommon/Options.swift
  10. 17 18
      FuzzTesting/Sources/FuzzJSON/main.swift
  11. 17 18
      FuzzTesting/Sources/FuzzTextFormat/main.swift
  12. 26 18
      FuzzTesting/Tests/FuzzCommonTests/Test_Options.swift
  13. 81 81
      Package.swift
  14. 124 125
      Performance/Harness.swift
  15. 1 2
      Performance/main.swift
  16. 9 9
      PluginExamples/Package.swift
  17. 2 3
      PluginExamples/Sources/ExampleTests/ExampleTests.swift
  18. 8 6
      Plugins/SwiftProtobufPlugin/plugin.swift
  19. 35 33
      Sources/Conformance/main.swift
  20. 425 407
      Sources/SwiftProtobuf/AnyMessageStorage.swift
  21. 10 10
      Sources/SwiftProtobuf/AnyUnpackError.swift
  22. 172 172
      Sources/SwiftProtobuf/AsyncMessageSequence.swift
  23. 123 101
      Sources/SwiftProtobuf/BinaryDecoder.swift
  24. 21 21
      Sources/SwiftProtobuf/BinaryDecodingError.swift
  25. 20 20
      Sources/SwiftProtobuf/BinaryDecodingOptions.swift
  26. 214 210
      Sources/SwiftProtobuf/BinaryDelimited.swift
  27. 5 3
      Sources/SwiftProtobuf/BinaryEncoder.swift
  28. 7 7
      Sources/SwiftProtobuf/BinaryEncodingError.swift
  29. 471 439
      Sources/SwiftProtobuf/BinaryEncodingSizeVisitor.swift
  30. 368 353
      Sources/SwiftProtobuf/BinaryEncodingVisitor.swift
  31. 15 15
      Sources/SwiftProtobuf/CustomJSONCodable.swift
  32. 180 163
      Sources/SwiftProtobuf/Decoder.swift
  33. 7 5
      Sources/SwiftProtobuf/DoubleParser.swift
  34. 58 56
      Sources/SwiftProtobuf/Enum.swift
  35. 10 5
      Sources/SwiftProtobuf/ExtensibleMessage.swift
  36. 58 53
      Sources/SwiftProtobuf/ExtensionFieldValueSet.swift
  37. 577 542
      Sources/SwiftProtobuf/ExtensionFields.swift
  38. 38 38
      Sources/SwiftProtobuf/FieldTag.swift
  39. 16 16
      Sources/SwiftProtobuf/FieldTypes.swift
  40. 131 128
      Sources/SwiftProtobuf/Google_Protobuf_Any+Extensions.swift
  41. 51 48
      Sources/SwiftProtobuf/Google_Protobuf_Any+Registry.swift
  42. 180 170
      Sources/SwiftProtobuf/Google_Protobuf_Duration+Extensions.swift
  43. 280 281
      Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift
  44. 57 57
      Sources/SwiftProtobuf/Google_Protobuf_ListValue+Extensions.swift
  45. 10 10
      Sources/SwiftProtobuf/Google_Protobuf_NullValue+Extensions.swift
  46. 56 56
      Sources/SwiftProtobuf/Google_Protobuf_Struct+Extensions.swift
  47. 274 265
      Sources/SwiftProtobuf/Google_Protobuf_Timestamp+Extensions.swift
  48. 129 129
      Sources/SwiftProtobuf/Google_Protobuf_Value+Extensions.swift
  49. 183 175
      Sources/SwiftProtobuf/Google_Protobuf_Wrappers+Extensions.swift
  50. 193 191
      Sources/SwiftProtobuf/HashVisitor.swift
  51. 32 27
      Sources/SwiftProtobuf/Internal.swift
  52. 731 722
      Sources/SwiftProtobuf/JSONDecoder.swift
  53. 12 12
      Sources/SwiftProtobuf/JSONDecodingOptions.swift
  54. 35 36
      Sources/SwiftProtobuf/JSONEncoder.swift
  55. 23 23
      Sources/SwiftProtobuf/JSONEncodingOptions.swift
  56. 414 402
      Sources/SwiftProtobuf/JSONEncodingVisitor.swift
  57. 161 161
      Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift
  58. 1261 1230
      Sources/SwiftProtobuf/JSONScanner.swift
  59. 2 2
      Sources/SwiftProtobuf/MathUtils.swift
  60. 29 29
      Sources/SwiftProtobuf/Message+AnyAdditions.swift
  61. 129 127
      Sources/SwiftProtobuf/Message+BinaryAdditions.swift
  62. 207 197
      Sources/SwiftProtobuf/Message+BinaryAdditions_Data.swift
  63. 91 90
      Sources/SwiftProtobuf/Message+FieldMask.swift
  64. 124 120
      Sources/SwiftProtobuf/Message+JSONAdditions.swift
  65. 42 43
      Sources/SwiftProtobuf/Message+JSONAdditions_Data.swift
  66. 123 115
      Sources/SwiftProtobuf/Message+JSONArrayAdditions.swift
  67. 54 50
      Sources/SwiftProtobuf/Message+JSONArrayAdditions_Data.swift
  68. 89 85
      Sources/SwiftProtobuf/Message+TextFormatAdditions.swift
  69. 152 152
      Sources/SwiftProtobuf/Message.swift
  70. 1 1
      Sources/SwiftProtobuf/MessageExtension.swift
  71. 226 220
      Sources/SwiftProtobuf/NameMap.swift
  72. 404 401
      Sources/SwiftProtobuf/PathDecoder.swift
  73. 226 226
      Sources/SwiftProtobuf/PathVisitor.swift
  74. 3 4
      Sources/SwiftProtobuf/ProtoNameProviding.swift
  75. 1 2
      Sources/SwiftProtobuf/ProtobufAPIVersionCheck.swift
  76. 6 9
      Sources/SwiftProtobuf/ProtobufMap.swift
  77. 213 212
      Sources/SwiftProtobuf/SelectiveVisitor.swift
  78. 4 5
      Sources/SwiftProtobuf/SimpleExtensionMap.swift
  79. 39 41
      Sources/SwiftProtobuf/StringUtils.swift
  80. 1 1
      Sources/SwiftProtobuf/SwiftProtobufContiguousBytes.swift
  81. 59 59
      Sources/SwiftProtobuf/SwiftProtobufError.swift
  82. 41 17
      Sources/SwiftProtobuf/TextFormatDecoder.swift
  83. 19 19
      Sources/SwiftProtobuf/TextFormatDecodingOptions.swift
  84. 32 33
      Sources/SwiftProtobuf/TextFormatEncoder.swift
  85. 3 3
      Sources/SwiftProtobuf/TextFormatEncodingOptions.swift
  86. 661 648
      Sources/SwiftProtobuf/TextFormatEncodingVisitor.swift
  87. 406 388
      Sources/SwiftProtobuf/TextFormatScanner.swift
  88. 12 12
      Sources/SwiftProtobuf/TimeUtils.swift
  89. 13 13
      Sources/SwiftProtobuf/UnknownStorage.swift
  90. 2 3
      Sources/SwiftProtobuf/UnsafeRawPointer+Shims.swift
  91. 79 80
      Sources/SwiftProtobuf/Varint.swift
  92. 8 8
      Sources/SwiftProtobuf/Version.swift
  93. 696 685
      Sources/SwiftProtobuf/Visitor.swift
  94. 45 47
      Sources/SwiftProtobuf/WireFormat.swift
  95. 4 5
      Sources/SwiftProtobuf/ZigZag.swift
  96. 269 254
      Sources/SwiftProtobufPluginLibrary/CodeGenerator.swift
  97. 16 16
      Sources/SwiftProtobufPluginLibrary/CodeGeneratorParameter.swift
  98. 202 200
      Sources/SwiftProtobufPluginLibrary/CodePrinter.swift
  99. 136 134
      Sources/SwiftProtobufPluginLibrary/Descriptor+Extensions.swift
  100. 714 676
      Sources/SwiftProtobufPluginLibrary/Descriptor.swift

+ 5 - 5
CompileTests/InternalImportsByDefault/Package.swift

@@ -20,23 +20,23 @@ let package = Package(
         .executableTarget(
             name: "InternalImportsByDefault",
             dependencies: [
-                .product(name: "SwiftProtobuf", package: "swift-protobuf"),
+                .product(name: "SwiftProtobuf", package: "swift-protobuf")
             ],
             exclude: [
                 "Protos/SomeProtoWithBytes.proto",
-                "Protos/ServiceOnly.proto"
+                "Protos/ServiceOnly.proto",
             ],
             swiftSettings: [
                 .enableExperimentalFeature("InternalImportsByDefault"),
                 .enableExperimentalFeature("AccessLevelOnImport"),
                 // Enable warnings as errors so the build fails if warnings are
                 // present in generated code.
-                    .unsafeFlags(["-warnings-as-errors"])
+                .unsafeFlags(["-warnings-as-errors"]),
             ],
             plugins: [
                 .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf")
             ]
-        ),
+        )
     ]
 )
 #else
@@ -48,7 +48,7 @@ let package = Package(
             exclude: [
                 "swift-protobuf-config.json",
                 "Protos/SomeProtoWithBytes.proto",
-                "Protos/ServiceOnly.proto"
+                "Protos/ServiceOnly.proto",
             ]
         )
     ]

+ 1 - 1
CompileTests/MultiModule/Package.swift

@@ -19,7 +19,7 @@ let package = Package(
         .target(
             name: "ModuleA",
             dependencies: [
-                .product(name: "SwiftProtobuf", package: "swift-protobuf"),
+                .product(name: "SwiftProtobuf", package: "swift-protobuf")
             ]
         ),
         .target(

+ 23 - 23
CompileTests/MultiModule/Tests/Test1/test1.swift

@@ -1,31 +1,31 @@
 import ImportsAPublicly
-// Don't need to import ModuleA because of the file being a `import public`
-
 import XCTest
 
-final class ExampleTests: XCTestCase {
-  func testA() {
-    let anA = A.with { $0.e = .a }
-    XCTAssertEqual(anA.e, .a)
-  }
+// Don't need to import ModuleA because of the file being a `import public`
 
-  func testImportsAPublicly() {
-    let imports = ImportsAPublicly.with { $0.a.e = .a }
-    XCTAssertEqual(imports.a.e, .a)
-  }
+final class ExampleTests: XCTestCase {
+    func testA() {
+        let anA = A.with { $0.e = .a }
+        XCTAssertEqual(anA.e, .a)
+    }
 
-  func testInterop() {
-    let anA = A.with { $0.e = .b }
-    let imports = ImportsAPublicly.with {
-      $0.a = anA
-      $0.e = .b
+    func testImportsAPublicly() {
+        let imports = ImportsAPublicly.with { $0.a.e = .a }
+        XCTAssertEqual(imports.a.e, .a)
     }
-    XCTAssertEqual(imports.a.e, imports.e)
-    let transitively = UsesATransitively.with {
-      $0.a = anA
-      $0.e = imports.e
+
+    func testInterop() {
+        let anA = A.with { $0.e = .b }
+        let imports = ImportsAPublicly.with {
+            $0.a = anA
+            $0.e = .b
+        }
+        XCTAssertEqual(imports.a.e, imports.e)
+        let transitively = UsesATransitively.with {
+            $0.a = anA
+            $0.e = imports.e
+        }
+        XCTAssertEqual(transitively.a, anA)
+        XCTAssertEqual(transitively.e, imports.e)
     }
-    XCTAssertEqual(transitively.a, anA)
-    XCTAssertEqual(transitively.e, imports.e)
-  }
 }

+ 23 - 23
CompileTests/MultiModule/Tests/Test2/test2.swift

@@ -1,31 +1,31 @@
 import ImportsImportsAPublicly
-// Don't need to import ModuleA because of the file being a `import public`
-
 import XCTest
 
-final class ExampleTests: XCTestCase {
-  func testA() {
-    let anA = A.with { $0.e = .a }
-    XCTAssertEqual(anA.e, .a)
-  }
+// Don't need to import ModuleA because of the file being a `import public`
 
-  func testImportsImportsAPublicly() {
-    let imports = ImportsImportsAPublicly.with { $0.a.e = .a }
-    XCTAssertEqual(imports.a.e, .a)
-  }
+final class ExampleTests: XCTestCase {
+    func testA() {
+        let anA = A.with { $0.e = .a }
+        XCTAssertEqual(anA.e, .a)
+    }
 
-  func testInterop() {
-    let anA = A.with { $0.e = .b }
-    let imports = ImportsImportsAPublicly.with {
-      $0.a = anA
-      $0.e = .b
+    func testImportsImportsAPublicly() {
+        let imports = ImportsImportsAPublicly.with { $0.a.e = .a }
+        XCTAssertEqual(imports.a.e, .a)
     }
-    XCTAssertEqual(imports.a.e, imports.e)
-    let transitively = UsesATransitively2.with {
-      $0.a = anA
-      $0.e = imports.e
+
+    func testInterop() {
+        let anA = A.with { $0.e = .b }
+        let imports = ImportsImportsAPublicly.with {
+            $0.a = anA
+            $0.e = .b
+        }
+        XCTAssertEqual(imports.a.e, imports.e)
+        let transitively = UsesATransitively2.with {
+            $0.a = anA
+            $0.e = imports.e
+        }
+        XCTAssertEqual(transitively.a, anA)
+        XCTAssertEqual(transitively.e, imports.e)
     }
-    XCTAssertEqual(transitively.a, anA)
-    XCTAssertEqual(transitively.e, imports.e)
-  }
 }

+ 16 - 9
FuzzTesting/Package.swift

@@ -6,32 +6,39 @@ import PackageDescription
 let package = Package(
     name: "FuzzTesting",
     platforms: [
-        .macOS(.v10_15),
+        .macOS(.v10_15)
     ],
     dependencies: [
-        .package(name: "SwiftProtobuf", path: ".."),
+        .package(name: "SwiftProtobuf", path: "..")
     ],
     targets: [
         .target(
             name: "FuzzCommon",
-            dependencies: ["SwiftProtobuf"]),
+            dependencies: ["SwiftProtobuf"]
+        ),
         .target(
             name: "FuzzBinary",
-            dependencies: ["SwiftProtobuf", "FuzzCommon"]),
+            dependencies: ["SwiftProtobuf", "FuzzCommon"]
+        ),
         .target(
             name: "FuzzBinaryDelimited",
-            dependencies: ["SwiftProtobuf", "FuzzCommon"]),
+            dependencies: ["SwiftProtobuf", "FuzzCommon"]
+        ),
         .target(
             name: "FuzzAsyncMessageSequence",
-            dependencies: ["SwiftProtobuf", "FuzzCommon"]),
+            dependencies: ["SwiftProtobuf", "FuzzCommon"]
+        ),
         .target(
             name: "FuzzJSON",
-            dependencies: ["SwiftProtobuf", "FuzzCommon"]),
+            dependencies: ["SwiftProtobuf", "FuzzCommon"]
+        ),
         .target(
             name: "FuzzTextFormat",
-            dependencies: ["SwiftProtobuf", "FuzzCommon"]),
+            dependencies: ["SwiftProtobuf", "FuzzCommon"]
+        ),
         .testTarget(
             name: "FuzzCommonTests",
-            dependencies: ["FuzzCommon"]),
+            dependencies: ["FuzzCommon"]
+        ),
     ]
 )

+ 33 - 34
FuzzTesting/Sources/FuzzAsyncMessageSequence/main.swift

@@ -7,49 +7,48 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-
 import FuzzCommon
-
 import SwiftProtobuf
 
-fileprivate func asyncByteStream(bytes: UnsafeRawBufferPointer) -> AsyncStream<UInt8> {
-  AsyncStream(UInt8.self) { continuation in
-    for i in 0..<bytes.count {
-      continuation.yield(bytes.loadUnaligned(fromByteOffset: i, as: UInt8.self))
+private func asyncByteStream(bytes: UnsafeRawBufferPointer) -> AsyncStream<UInt8> {
+    AsyncStream(UInt8.self) { continuation in
+        for i in 0..<bytes.count {
+            continuation.yield(bytes.loadUnaligned(fromByteOffset: i, as: UInt8.self))
+        }
+        continuation.finish()
     }
-    continuation.finish()
-  }
 }
 
 @_cdecl("LLVMFuzzerTestOneInput")
 public func FuzzAsyncMessageSequence(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
-  // No decoding options here, a leading zero is actually valid (zero length message),
-  // so we rely on the other Binary fuzz tester to test options, and just let this
-  // one focus on issue around framing of the messages on the stream.
-  let bytes = UnsafeRawBufferPointer(start: start, count: count)
-  let asyncBytes = asyncByteStream(bytes: bytes)
-  let decoding = asyncBytes.binaryProtobufDelimitedMessages(
-    of: SwiftProtoTesting_Fuzz_Message.self,
-    extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions)
+    // No decoding options here, a leading zero is actually valid (zero length message),
+    // so we rely on the other Binary fuzz tester to test options, and just let this
+    // one focus on issue around framing of the messages on the stream.
+    let bytes = UnsafeRawBufferPointer(start: start, count: count)
+    let asyncBytes = asyncByteStream(bytes: bytes)
+    let decoding = asyncBytes.binaryProtobufDelimitedMessages(
+        of: SwiftProtoTesting_Fuzz_Message.self,
+        extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions
+    )
 
-  let semaphore = DispatchSemaphore(value: 0)
-  Task {
-    do {
-      for try await _ in decoding {
-        // TODO: Test serialization for completeness.
-        // We could serialize individual messages like this:
-        // let _: [UInt8] = try! msg.serializedBytes()
-        // but we don't have a stream writer which is what
-        // we really want to exercise here.
+    let semaphore = DispatchSemaphore(value: 0)
+    Task {
+        do {
+            for try await _ in decoding {
+                // TODO: Test serialization for completeness.
+                // We could serialize individual messages like this:
+                // let _: [UInt8] = try! msg.serializedBytes()
+                // but we don't have a stream writer which is what
+                // we really want to exercise here.
 
-        // Also, serialization here more than doubles the total
-        // run time, leading to timeouts for the fuzz tester. :(
-      }
-    } catch {
-      // Error parsing are to be expected since not all input will be well formed.
+                // Also, serialization here more than doubles the total
+                // run time, leading to timeouts for the fuzz tester. :(
+            }
+        } catch {
+            // Error parsing are to be expected since not all input will be well formed.
+        }
+        semaphore.signal()
     }
-    semaphore.signal()
-  }
-  semaphore.wait()
-  return 0
+    semaphore.wait()
+    return 0
 }

+ 17 - 17
FuzzTesting/Sources/FuzzBinary/main.swift

@@ -7,26 +7,26 @@
 // -----------------------------------------------------------------------------
 
 import FuzzCommon
-
 import SwiftProtobuf
 
 @_cdecl("LLVMFuzzerTestOneInput")
 public func FuzzBinary(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
-  guard let (options, bytes) = BinaryDecodingOptions.extractOptions(start, count) else {
-    return 1
-  }
-  var msg: SwiftProtoTesting_Fuzz_Message?
-  do {
-    msg = try SwiftProtoTesting_Fuzz_Message(
-      serializedBytes: Array(bytes),
-      extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions,
-      options: options)
-  } catch {
-    // Error parsing are to be expected since not all input will be well formed.
-  }
-  // Test serialization for completeness.
-  // If a message was parsed, it should not fail to serialize, so assert as such.
-  let _: [UInt8]? = try! msg?.serializedBytes()
+    guard let (options, bytes) = BinaryDecodingOptions.extractOptions(start, count) else {
+        return 1
+    }
+    var msg: SwiftProtoTesting_Fuzz_Message?
+    do {
+        msg = try SwiftProtoTesting_Fuzz_Message(
+            serializedBytes: Array(bytes),
+            extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions,
+            options: options
+        )
+    } catch {
+        // Error parsing are to be expected since not all input will be well formed.
+    }
+    // Test serialization for completeness.
+    // If a message was parsed, it should not fail to serialize, so assert as such.
+    let _: [UInt8]? = try! msg?.serializedBytes()
 
-  return 0
+    return 0
 }

+ 29 - 30
FuzzTesting/Sources/FuzzBinaryDelimited/main.swift

@@ -7,41 +7,40 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-
 import FuzzCommon
-
 import SwiftProtobuf
 
 @_cdecl("LLVMFuzzerTestOneInput")
 public func FuzzDelimited(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
-  // No decoding options here, a leading zero is actually valid (zero length message),
-  // so we rely on the other Binary fuzz tester to test options, and just let this
-  // one focus on issue around framing of the messages on the stream.
-  let bytes = UnsafeRawBufferPointer(start: start, count: count)
-  let istream = InputStream(data: Data(bytes))
-  istream.open()
-  while true {
-    let msg: SwiftProtoTesting_Fuzz_Message?
-    do {
-      msg = try BinaryDelimited.parse(
-        messageType: SwiftProtoTesting_Fuzz_Message.self,
-        from: istream,
-        extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions)
-    } catch {
-      // Error parsing are to be expected since not all input will be well formed.
-      break
-    }
-    // Test serialization for completeness.
-    // If a message was parsed, it should not fail to serialize, so assert as such.
-    if let msg = msg {
-      // Could use one stream for all messages, but since fuzz tests have
-      // memory limits, attempt to avoid hitting that limit with a new stream
-      // for each output attempt.
-      let ostream = OutputStream.toMemory()
-      ostream.open()
-      try! BinaryDelimited.serialize(message: msg, to: ostream)
+    // No decoding options here, a leading zero is actually valid (zero length message),
+    // so we rely on the other Binary fuzz tester to test options, and just let this
+    // one focus on issue around framing of the messages on the stream.
+    let bytes = UnsafeRawBufferPointer(start: start, count: count)
+    let istream = InputStream(data: Data(bytes))
+    istream.open()
+    while true {
+        let msg: SwiftProtoTesting_Fuzz_Message?
+        do {
+            msg = try BinaryDelimited.parse(
+                messageType: SwiftProtoTesting_Fuzz_Message.self,
+                from: istream,
+                extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions
+            )
+        } catch {
+            // Error parsing are to be expected since not all input will be well formed.
+            break
+        }
+        // Test serialization for completeness.
+        // If a message was parsed, it should not fail to serialize, so assert as such.
+        if let msg = msg {
+            // Could use one stream for all messages, but since fuzz tests have
+            // memory limits, attempt to avoid hitting that limit with a new stream
+            // for each output attempt.
+            let ostream = OutputStream.toMemory()
+            ostream.open()
+            try! BinaryDelimited.serialize(message: msg, to: ostream)
+        }
     }
-  }
 
-  return 0
+    return 0
 }

+ 3 - 4
FuzzTesting/Sources/FuzzCommon/Options.swift

@@ -7,7 +7,6 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-
 import SwiftProtobuf
 
 public enum FuzzOption<T: SupportsFuzzOptions> {
@@ -105,7 +104,7 @@ extension SupportsFuzzOptions {
 
 extension BinaryDecodingOptions: SupportsFuzzOptions {
     public static var fuzzOptionsList: [FuzzOption<Self>] {
-        return [
+        [
             // NOTE: Do not reorder these in the future as it invalidates all
             // existing cases.
 
@@ -119,7 +118,7 @@ extension BinaryDecodingOptions: SupportsFuzzOptions {
 
 extension JSONDecodingOptions: SupportsFuzzOptions {
     public static var fuzzOptionsList: [FuzzOption<Self>] {
-        return [
+        [
             // NOTE: Do not reorder these in the future as it invalidates all
             // existing cases.
 
@@ -133,7 +132,7 @@ extension JSONDecodingOptions: SupportsFuzzOptions {
 
 extension TextFormatDecodingOptions: SupportsFuzzOptions {
     public static var fuzzOptionsList: [FuzzOption<Self>] {
-        return [
+        [
             // NOTE: Do not reorder these in the future as it invalidates all
             // existing cases.
 

+ 17 - 18
FuzzTesting/Sources/FuzzJSON/main.swift

@@ -7,27 +7,26 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-
 import FuzzCommon
-
 import SwiftProtobuf
 
 @_cdecl("LLVMFuzzerTestOneInput")
 public func FuzzJSON(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
-  guard let (options, bytes) = JSONDecodingOptions.extractOptions(start, count) else {
-    return 1
-  }
-  var msg: SwiftProtoTesting_Fuzz_Message?
-  do {
-    msg = try SwiftProtoTesting_Fuzz_Message(
-      jsonUTF8Data: Data(bytes),
-      extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions,
-      options: options)
-  } catch {
-    // Error parsing are to be expected since not all input will be well formed.
-  }
-  // Test serialization for completeness.
-  // If a message was parsed, it should not fail to serialize, so assert as such.
-  let _ = try! msg?.jsonString()
-  return 0
+    guard let (options, bytes) = JSONDecodingOptions.extractOptions(start, count) else {
+        return 1
+    }
+    var msg: SwiftProtoTesting_Fuzz_Message?
+    do {
+        msg = try SwiftProtoTesting_Fuzz_Message(
+            jsonUTF8Data: Data(bytes),
+            extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions,
+            options: options
+        )
+    } catch {
+        // Error parsing are to be expected since not all input will be well formed.
+    }
+    // Test serialization for completeness.
+    // If a message was parsed, it should not fail to serialize, so assert as such.
+    let _ = try! msg?.jsonString()
+    return 0
 }

+ 17 - 18
FuzzTesting/Sources/FuzzTextFormat/main.swift

@@ -7,28 +7,27 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-
 import FuzzCommon
-
 import SwiftProtobuf
 
 @_cdecl("LLVMFuzzerTestOneInput")
 public func FuzzTextFormat(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
-  guard let (options, bytes) = TextFormatDecodingOptions.extractOptions(start, count) else {
-    return 1
-  }
-  guard let str = String(data: Data(bytes), encoding: .utf8) else { return 0 }
-  var msg: SwiftProtoTesting_Fuzz_Message?
-  do {
-    msg = try SwiftProtoTesting_Fuzz_Message(
-      textFormatString: str,
-      options: options,
-      extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions)
-  } catch {
-    // Error parsing are to be expected since not all input will be well formed.
-  }
-  // Test serialization for completeness.
-  let _ = msg?.textFormatString()
+    guard let (options, bytes) = TextFormatDecodingOptions.extractOptions(start, count) else {
+        return 1
+    }
+    guard let str = String(data: Data(bytes), encoding: .utf8) else { return 0 }
+    var msg: SwiftProtoTesting_Fuzz_Message?
+    do {
+        msg = try SwiftProtoTesting_Fuzz_Message(
+            textFormatString: str,
+            options: options,
+            extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions
+        )
+    } catch {
+        // Error parsing are to be expected since not all input will be well formed.
+    }
+    // Test serialization for completeness.
+    let _ = msg?.textFormatString()
 
-  return 0
+    return 0
 }

+ 26 - 18
FuzzTesting/Tests/FuzzCommonTests/Test_Options.swift

@@ -7,11 +7,10 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-import XCTest
-
 import FuzzCommon
+import XCTest
 
-struct TestOptions : SupportsFuzzOptions {
+struct TestOptions: SupportsFuzzOptions {
 
     var bool1: Bool = false {
         didSet { sets.append("bool1:\(bool1)") }
@@ -40,7 +39,7 @@ struct TestOptions : SupportsFuzzOptions {
     init() {}
 }
 
-struct TestOptionsLarge : SupportsFuzzOptions {
+struct TestOptionsLarge: SupportsFuzzOptions {
 
     var bool1: Bool = false {
         didSet { sets.append("bool1:\(bool1)") }
@@ -95,7 +94,7 @@ final class Test_FuzzOptions: XCTestCase {
 
     func testOptionBasics_noOptionsSignal() throws {
         // Claim no bytes passed.
-        let bytes: [UInt8] = [ ]
+        let bytes: [UInt8] = []
         XCTAssertEqual(bytes.count, 0)
         try bytes.withUnsafeBytes { ptr in
             let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
@@ -106,7 +105,7 @@ final class Test_FuzzOptions: XCTestCase {
 
         // Try with no leading zero, so no options.
         for x: UInt8 in 1...UInt8.max {
-            let bytes: [UInt8] = [ x ]
+            let bytes: [UInt8] = [x]
             XCTAssertEqual(bytes.count, 1)
             try bytes.withUnsafeBytes { ptr in
                 let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
@@ -120,7 +119,7 @@ final class Test_FuzzOptions: XCTestCase {
     }
 
     func testOptionBasics_optionsSignalNoBytes() throws {
-        let bytes: [UInt8] = [ 0 ]  // Options signal, then nothing
+        let bytes: [UInt8] = [0]  // Options signal, then nothing
         XCTAssertEqual(bytes.count, 1)
         try bytes.withUnsafeBytes { ptr in
             let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
@@ -140,7 +139,7 @@ final class Test_FuzzOptions: XCTestCase {
             (0x3, true, true, ["bool1:true", "bool2:true"]),
         ]
         for test in testCases {
-            let bytes: [UInt8] = [ 0, test.byte]
+            let bytes: [UInt8] = [0, test.byte]
             XCTAssertEqual(bytes.count, 2)
             try bytes.withUnsafeBytes { ptr in
                 let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
@@ -162,7 +161,7 @@ final class Test_FuzzOptions: XCTestCase {
             ([0xC, 3, 20], 3, 4, ["int1:3", "int2:4"]),  // int2 has a mod applied
         ]
         for test in testCases {
-            let bytes: [UInt8] = [ 0 ] + test.bytes
+            let bytes: [UInt8] = [0] + test.bytes
             try bytes.withUnsafeBytes { ptr in
                 let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
                 let (opts, bytes) = try XCTUnwrap(result)
@@ -183,7 +182,7 @@ final class Test_FuzzOptions: XCTestCase {
             [0xC, 20],  // int1 & int2, data for only int1
         ]
         for test in testCases {
-            let bytes: [UInt8] = [ 0 ] + test
+            let bytes: [UInt8] = [0] + test
             bytes.withUnsafeBytes { ptr in
                 XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count))
             }
@@ -194,7 +193,7 @@ final class Test_FuzzOptions: XCTestCase {
         // Try every value that will have at least one bit set above the valid ones
         // to ensure it causing parsing failure.
         for x: UInt8 in 0x10...UInt8.max {
-            let bytes: [UInt8] = [ 0, x ]
+            let bytes: [UInt8] = [0, x]
             bytes.withUnsafeBytes { ptr in
                 XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count))
             }
@@ -205,7 +204,7 @@ final class Test_FuzzOptions: XCTestCase {
         // For the first byte of optionBits, just signal that there is a second, but
         // then set all the expected zero bits to ensure it fails.
         for x: UInt8 in 0x8...UInt8.max {
-            let bytes: [UInt8] = [ 0, 0x80, x ]
+            let bytes: [UInt8] = [0, 0x80, x]
             bytes.withUnsafeBytes { ptr in
                 XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count))
             }
@@ -213,7 +212,7 @@ final class Test_FuzzOptions: XCTestCase {
     }
 
     func testOptionBasics_bytesAfterOptsComeThrough() throws {
-        let bytes: [UInt8] = [ 0, 0, 1, 2, 3]
+        let bytes: [UInt8] = [0, 0, 1, 2, 3]
         XCTAssertEqual(bytes.count, 5)
         try bytes.withUnsafeBytes { ptr in
             let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
@@ -227,7 +226,7 @@ final class Test_FuzzOptions: XCTestCase {
         }
 
         // Make sure data is right after a bytes value also
-        let bytes2: [UInt8] = [ 0, 0x4, 20, 4, 15, 26]
+        let bytes2: [UInt8] = [0, 0x4, 20, 4, 15, 26]
         try bytes2.withUnsafeBytes { ptr in
             let result = TestOptions.extractOptions(ptr.baseAddress!, bytes2.count)
             let (opts, bytes) = try XCTUnwrap(result)
@@ -242,12 +241,15 @@ final class Test_FuzzOptions: XCTestCase {
         // Options that can spill to two bytes for the optionBits.
 
         // Only one byte of optionsBits
-        let bytes3: [UInt8] = [ 0, 0, 1, 2, 3]
+        let bytes3: [UInt8] = [0, 0, 1, 2, 3]
         XCTAssertEqual(bytes3.count, 5)
         try bytes3.withUnsafeBytes { ptr in
             let result = TestOptionsLarge.extractOptions(ptr.baseAddress!, bytes3.count)
             let (opts, bytes) = try XCTUnwrap(result)
-            XCTAssertEqual(opts.sets, ["bool1:false", "bool2:false", "bool3:false", "bool4:false", "bool5:false", "bool6:false"])
+            XCTAssertEqual(
+                opts.sets,
+                ["bool1:false", "bool2:false", "bool3:false", "bool4:false", "bool5:false", "bool6:false"]
+            )
             XCTAssertEqual(bytes.count, 3)
             XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
             XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 1)
@@ -256,12 +258,18 @@ final class Test_FuzzOptions: XCTestCase {
         }
 
         // Two bytes of optionsBits with a `byte` value
-        let bytes4: [UInt8] = [ 0, 0x90, 123, 0x4, 20, 81, 92, 103]
+        let bytes4: [UInt8] = [0, 0x90, 123, 0x4, 20, 81, 92, 103]
         XCTAssertEqual(bytes4.count, 8)
         try bytes4.withUnsafeBytes { ptr in
             let result = TestOptionsLarge.extractOptions(ptr.baseAddress!, bytes4.count)
             let (opts, bytes) = try XCTUnwrap(result)
-            XCTAssertEqual(opts.sets, ["bool1:false", "bool2:false", "bool3:false", "bool4:false", "int1:123", "bool5:false", "bool6:false", "bool7:false", "bool8:false", "int2:20"])
+            XCTAssertEqual(
+                opts.sets,
+                [
+                    "bool1:false", "bool2:false", "bool3:false", "bool4:false", "int1:123", "bool5:false",
+                    "bool6:false", "bool7:false", "bool8:false", "int2:20",
+                ]
+            )
             XCTAssertEqual(bytes.count, 3)
             XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
             XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 81)

+ 81 - 81
Package.swift

@@ -12,90 +12,90 @@
 import PackageDescription
 
 let package = Package(
-  name: "SwiftProtobuf",
-  products: [
-    .executable(
-        name: "protoc-gen-swift",
-        targets: ["protoc-gen-swift"]
-    ),
-    .library(
-        name: "SwiftProtobuf",
-        targets: ["SwiftProtobuf"]
-    ),
-    .library(
-        name: "SwiftProtobufPluginLibrary",
-        targets: ["SwiftProtobufPluginLibrary"]
-    ),
-    .plugin(
-        name: "SwiftProtobufPlugin",
-        targets: ["SwiftProtobufPlugin"]
-    ),
-  ],
-  dependencies: [
-    .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0"),
-  ],
-  targets: [
-    .target(
-        name: "SwiftProtobuf",
-        exclude: ["CMakeLists.txt"],
-        resources: [.copy("PrivacyInfo.xcprivacy")],
-        swiftSettings: .packageSettings
-    ),
-    .target(
-        name: "SwiftProtobufPluginLibrary",
-        dependencies: ["SwiftProtobuf"],
-        exclude: ["CMakeLists.txt"],
-        resources: [.copy("PrivacyInfo.xcprivacy")],
-        swiftSettings: .packageSettings
-    ),
-    .target(
-        name: "SwiftProtobufTestHelpers",
-        dependencies: ["SwiftProtobuf"],
-        swiftSettings: .packageSettings
-    ),
-    .executableTarget(
-        name: "protoc-gen-swift",
-        dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobuf"],
-        exclude: ["CMakeLists.txt"],
-        swiftSettings: .packageSettings
-    ),
-    .executableTarget(
-        name: "Conformance",
-        dependencies: ["SwiftProtobuf"],
-        exclude: ["failure_list_swift.txt", "text_format_failure_list_swift.txt"],
-        swiftSettings: .packageSettings
-    ),
-    .plugin(
-        name: "SwiftProtobufPlugin",
-        capability: .buildTool(),
-        dependencies: ["protoc-gen-swift"]
-    ),
-    .testTarget(
-        name: "SwiftProtobufTests",
-        dependencies: ["SwiftProtobuf"],
-        swiftSettings: .packageSettings
-    ),
-    .testTarget(
-        name: "SwiftProtobufPluginLibraryTests",
-        dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobufTestHelpers"],
-        swiftSettings: .packageSettings
-    ),
-    .testTarget(
-        name: "protoc-gen-swiftTests",
-        dependencies: ["protoc-gen-swift", "SwiftProtobufTestHelpers"],
-        swiftSettings: .packageSettings
-    ),
-  ],
-  swiftLanguageVersions: [.v5]
+    name: "SwiftProtobuf",
+    products: [
+        .executable(
+            name: "protoc-gen-swift",
+            targets: ["protoc-gen-swift"]
+        ),
+        .library(
+            name: "SwiftProtobuf",
+            targets: ["SwiftProtobuf"]
+        ),
+        .library(
+            name: "SwiftProtobufPluginLibrary",
+            targets: ["SwiftProtobufPluginLibrary"]
+        ),
+        .plugin(
+            name: "SwiftProtobufPlugin",
+            targets: ["SwiftProtobufPlugin"]
+        ),
+    ],
+    dependencies: [
+        .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0")
+    ],
+    targets: [
+        .target(
+            name: "SwiftProtobuf",
+            exclude: ["CMakeLists.txt"],
+            resources: [.copy("PrivacyInfo.xcprivacy")],
+            swiftSettings: .packageSettings
+        ),
+        .target(
+            name: "SwiftProtobufPluginLibrary",
+            dependencies: ["SwiftProtobuf"],
+            exclude: ["CMakeLists.txt"],
+            resources: [.copy("PrivacyInfo.xcprivacy")],
+            swiftSettings: .packageSettings
+        ),
+        .target(
+            name: "SwiftProtobufTestHelpers",
+            dependencies: ["SwiftProtobuf"],
+            swiftSettings: .packageSettings
+        ),
+        .executableTarget(
+            name: "protoc-gen-swift",
+            dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobuf"],
+            exclude: ["CMakeLists.txt"],
+            swiftSettings: .packageSettings
+        ),
+        .executableTarget(
+            name: "Conformance",
+            dependencies: ["SwiftProtobuf"],
+            exclude: ["failure_list_swift.txt", "text_format_failure_list_swift.txt"],
+            swiftSettings: .packageSettings
+        ),
+        .plugin(
+            name: "SwiftProtobufPlugin",
+            capability: .buildTool(),
+            dependencies: ["protoc-gen-swift"]
+        ),
+        .testTarget(
+            name: "SwiftProtobufTests",
+            dependencies: ["SwiftProtobuf"],
+            swiftSettings: .packageSettings
+        ),
+        .testTarget(
+            name: "SwiftProtobufPluginLibraryTests",
+            dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobufTestHelpers"],
+            swiftSettings: .packageSettings
+        ),
+        .testTarget(
+            name: "protoc-gen-swiftTests",
+            dependencies: ["protoc-gen-swift", "SwiftProtobufTestHelpers"],
+            swiftSettings: .packageSettings
+        ),
+    ],
+    swiftLanguageVersions: [.v5]
 )
 
 // Settings for every Swift target in this package, like project-level settings
 // in an Xcode project.
 extension Array where Element == PackageDescription.SwiftSetting {
-  static var packageSettings: Self {
-    [
-      .enableExperimentalFeature("StrictConcurrency=complete"),
-      .enableUpcomingFeature("ExistentialAny"),
-    ]
-  }
+    static var packageSettings: Self {
+        [
+            .enableExperimentalFeature("StrictConcurrency=complete"),
+            .enableUpcomingFeature("ExistentialAny"),
+        ]
+    }
 }

+ 124 - 125
Performance/Harness.swift

@@ -15,17 +15,17 @@
 import Foundation
 
 private func padded(_ input: String, to width: Int) -> String {
-  return input + String(repeating: " ", count: max(0, width - input.count))
+    input + String(repeating: " ", count: max(0, width - input.count))
 }
 
 /// It is expected that the generator will provide these in an extension.
 protocol GeneratedHarnessMembers {
-  /// The number of times to loop the body of the run() method.
-  /// Increase this to get better precision.
-  var runCount: Int { get }
+    /// The number of times to loop the body of the run() method.
+    /// Increase this to get better precision.
+    var runCount: Int { get }
 
-  /// The main body of the performance harness.
-  func run()
+    /// The main body of the performance harness.
+    func run()
 }
 
 /// Harness used for performance tests.
@@ -34,135 +34,134 @@ protocol GeneratedHarnessMembers {
 /// run() method, which the main.swift file calls.
 class Harness: GeneratedHarnessMembers {
 
-  /// The number of times to execute the block passed to measure().
-  var measurementCount = 10
-
-  /// The number of times to call append() for repeated fields.
-  let repeatedCount: Int32 = 10
-
-  /// Ordered list of task names
-  var taskNames = [String]()
-
-  /// The times taken by subtasks during each measured attempt.
-  var subtaskTimings = [String: [TimeInterval]]()
-
-  /// Times for the subtasks in the current attempt.
-  var currentSubtasks = [String: TimeInterval]()
-
-  /// The file to which results should be written.
-  let resultsFile: FileHandle?
-
-  /// Creates a new harness that writes its statistics to the given file
-  /// (as well as to stdout).
-  init(resultsFile: FileHandle?) {
-    self.resultsFile = resultsFile
-  }
-
-  /// Measures the time it takes to execute the given block. The block is
-  /// executed five times and the mean/standard deviation are computed.
-  func measure(block: () throws -> Void) {
-    var timings = [TimeInterval]()
-    subtaskTimings.removeAll()
-    print("Running each check \(runCount) times, times in µs")
-
-    var headingsDisplayed = false
-
-    do {
-      // Do each measurement multiple times and collect the means and standard
-      // deviation to account for noise.
-      for attempt in 1...measurementCount {
-        currentSubtasks.removeAll()
-        taskNames.removeAll()
-        let start = Date()
-        for _ in 0..<runCount {
-          taskNames.removeAll()
-          try block()
-        }
-        let end = Date()
-        let diff = end.timeIntervalSince(start) * 1000
-        timings.append(diff)
-
-        if !headingsDisplayed {
-            let names = taskNames
-            print("   ", terminator: "")
-            for (i, name) in names.enumerated() {
-                if i % 2 == 0 {
-                    print(padded(name, to: 18), terminator: "")
+    /// The number of times to execute the block passed to measure().
+    var measurementCount = 10
+
+    /// The number of times to call append() for repeated fields.
+    let repeatedCount: Int32 = 10
+
+    /// Ordered list of task names
+    var taskNames = [String]()
+
+    /// The times taken by subtasks during each measured attempt.
+    var subtaskTimings = [String: [TimeInterval]]()
+
+    /// Times for the subtasks in the current attempt.
+    var currentSubtasks = [String: TimeInterval]()
+
+    /// The file to which results should be written.
+    let resultsFile: FileHandle?
+
+    /// Creates a new harness that writes its statistics to the given file
+    /// (as well as to stdout).
+    init(resultsFile: FileHandle?) {
+        self.resultsFile = resultsFile
+    }
+
+    /// Measures the time it takes to execute the given block. The block is
+    /// executed five times and the mean/standard deviation are computed.
+    func measure(block: () throws -> Void) {
+        var timings = [TimeInterval]()
+        subtaskTimings.removeAll()
+        print("Running each check \(runCount) times, times in µs")
+
+        var headingsDisplayed = false
+
+        do {
+            // Do each measurement multiple times and collect the means and standard
+            // deviation to account for noise.
+            for attempt in 1...measurementCount {
+                currentSubtasks.removeAll()
+                taskNames.removeAll()
+                let start = Date()
+                for _ in 0..<runCount {
+                    taskNames.removeAll()
+                    try block()
                 }
-            }
-            print()
-            print("   ", terminator: "")
-            print(padded("", to: 9), terminator: "")
-            for (i, name) in names.enumerated() {
-                if i % 2 == 1 {
-                    print(padded(name, to: 18), terminator: "")
+                let end = Date()
+                let diff = end.timeIntervalSince(start) * 1000
+                timings.append(diff)
+
+                if !headingsDisplayed {
+                    let names = taskNames
+                    print("   ", terminator: "")
+                    for (i, name) in names.enumerated() {
+                        if i % 2 == 0 {
+                            print(padded(name, to: 18), terminator: "")
+                        }
+                    }
+                    print()
+                    print("   ", terminator: "")
+                    print(padded("", to: 9), terminator: "")
+                    for (i, name) in names.enumerated() {
+                        if i % 2 == 1 {
+                            print(padded(name, to: 18), terminator: "")
+                        }
+                    }
+                    print()
+                    headingsDisplayed = true
                 }
+
+                print(String(format: "%3d", attempt), terminator: "")
+
+                for name in taskNames {
+                    let time = currentSubtasks[name] ?? 0
+                    print(String(format: "%9.3f", time), terminator: "")
+                    subtaskTimings[name] = (subtaskTimings[name] ?? []) + [time]
+                }
+                print()
             }
-            print()
-            headingsDisplayed = true
+        } catch let e {
+            fatalError("Generated harness threw an error: \(e)")
         }
 
-        print(String(format: "%3d", attempt), terminator: "")
-
-        for name in taskNames {
-          let time = currentSubtasks[name] ?? 0
-          print(String(format: "%9.3f", time), terminator: "")
-          subtaskTimings[name] = (subtaskTimings[name] ?? []) + [time]
+        for (name, times) in subtaskTimings {
+            writeToLog("\"\(name)\": \(times),\n")
         }
-        print()
-      }
-    } catch let e {
-      fatalError("Generated harness threw an error: \(e)")
+
+        let (mean, stddev) = statistics(timings)
+        let stats =
+            String(format: "Relative stddev = %.1f%%\n", (stddev / mean) * 100.0)
+        print(stats)
     }
 
-    for (name, times) in subtaskTimings {
-      writeToLog("\"\(name)\": \(times),\n")
+    /// Measure an individual subtask whose timing will be printed separately
+    /// from the main results.
+    func measureSubtask<Result>(
+        _ name: String,
+        block: () throws -> Result
+    ) rethrows -> Result {
+        try autoreleasepool { () -> Result in
+            taskNames.append(name)
+            let start = Date()
+            let result = try block()
+            let end = Date()
+            let diff = end.timeIntervalSince(start) / Double(runCount) * 1000000.0
+            currentSubtasks[name] = (currentSubtasks[name] ?? 0) + diff
+            return result
+        }
     }
 
-    let (mean, stddev) = statistics(timings)
-    let stats =
-        String(format: "Relative stddev = %.1f%%\n", (stddev / mean) * 100.0)
-    print(stats)
-  }
-
-  /// Measure an individual subtask whose timing will be printed separately
-  /// from the main results.
-  func measureSubtask<Result>(
-    _ name: String,
-    block: () throws -> Result
-  ) rethrows -> Result {
-      return try autoreleasepool { () -> Result in
-          taskNames.append(name)
-          let start = Date()
-          let result = try block()
-          let end = Date()
-          let diff = end.timeIntervalSince(start) / Double(runCount) * 1000000.0
-          currentSubtasks[name] = (currentSubtasks[name] ?? 0) + diff
-          return result
-      }
-  }
-
-  /// Compute the mean and standard deviation of the given time intervals.
-  private func statistics(_ timings: [TimeInterval]) ->
-    (mean: TimeInterval, stddev: TimeInterval) {
-    var sum: TimeInterval = 0
-    var sqsum: TimeInterval = 0
-    for timing in timings {
-      sum += timing
-      sqsum += timing * timing
+    /// Compute the mean and standard deviation of the given time intervals.
+    private func statistics(_ timings: [TimeInterval]) -> (mean: TimeInterval, stddev: TimeInterval) {
+        var sum: TimeInterval = 0
+        var sqsum: TimeInterval = 0
+        for timing in timings {
+            sum += timing
+            sqsum += timing * timing
+        }
+        let n = TimeInterval(timings.count)
+        let mean = sum / n
+        let variance = sqsum / n - mean * mean
+        return (mean: mean, stddev: sqrt(variance))
     }
-    let n = TimeInterval(timings.count)
-    let mean = sum / n
-    let variance = sqsum / n - mean * mean
-    return (mean: mean, stddev: sqrt(variance))
-  }
-
-  /// Writes a string to the data results file that will be parsed by the
-  /// calling script to produce visualizations.
-  private func writeToLog(_ string: String) {
-    if let resultsFile = resultsFile {
-      let utf8 = Data(string.utf8)
-      resultsFile.write(utf8)
+
+    /// Writes a string to the data results file that will be parsed by the
+    /// calling script to produce visualizations.
+    private func writeToLog(_ string: String) {
+        if let resultsFile = resultsFile {
+            let utf8 = Data(string.utf8)
+            resultsFile.write(utf8)
+        }
     }
-  }
 }

+ 1 - 2
Performance/main.swift

@@ -15,8 +15,7 @@
 import Foundation
 
 let args = CommandLine.arguments
-let resultsFile = args.count > 1 ?
-    FileHandle(forWritingAtPath: args[1]) : nil
+let resultsFile = args.count > 1 ? FileHandle(forWritingAtPath: args[1]) : nil
 resultsFile?.seekToEndOfFile()
 
 let harness = Harness(resultsFile: resultsFile)

+ 9 - 9
PluginExamples/Package.swift

@@ -16,9 +16,9 @@ private func targets() -> [Target] {
         .target(name: "Nested"),
         .target(name: "Import"),
     ]
-#if compiler(>=5.9)
+    #if compiler(>=5.9)
     testDependencies.append(.target(name: "AccessLevelOnImport"))
-#endif
+    #endif
     var targets: [Target] = [
         .testTarget(
             name: "ExampleTests",
@@ -27,7 +27,7 @@ private func targets() -> [Target] {
         .target(
             name: "Simple",
             dependencies: [
-                .product(name: "SwiftProtobuf", package: "swift-protobuf"),
+                .product(name: "SwiftProtobuf", package: "swift-protobuf")
             ],
             plugins: [
                 .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf")
@@ -36,7 +36,7 @@ private func targets() -> [Target] {
         .target(
             name: "Nested",
             dependencies: [
-                .product(name: "SwiftProtobuf", package: "swift-protobuf"),
+                .product(name: "SwiftProtobuf", package: "swift-protobuf")
             ],
             plugins: [
                 .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf")
@@ -45,28 +45,28 @@ private func targets() -> [Target] {
         .target(
             name: "Import",
             dependencies: [
-                .product(name: "SwiftProtobuf", package: "swift-protobuf"),
+                .product(name: "SwiftProtobuf", package: "swift-protobuf")
             ],
             plugins: [
                 .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf")
             ]
         ),
     ]
-#if compiler(>=5.9)
+    #if compiler(>=5.9)
     targets.append(
         .target(
             name: "AccessLevelOnImport",
             dependencies: [
-                .product(name: "SwiftProtobuf", package: "swift-protobuf"),
+                .product(name: "SwiftProtobuf", package: "swift-protobuf")
             ],
             swiftSettings: [
-                .enableExperimentalFeature("AccessLevelOnImport"),
+                .enableExperimentalFeature("AccessLevelOnImport")
             ],
             plugins: [
                 .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf")
             ]
         )
     )
-#endif
+    #endif
     return targets
 }

+ 2 - 3
PluginExamples/Sources/ExampleTests/ExampleTests.swift

@@ -1,7 +1,6 @@
-import Simple
-import Nested
 import Import
-
+import Nested
+import Simple
 import XCTest
 
 final class ExampleTests: XCTestCase {

+ 8 - 6
Plugins/SwiftProtobufPlugin/plugin.swift

@@ -112,11 +112,13 @@ struct SwiftProtobufPlugin {
         sourceFiles: FileList,
         tool: (String) throws -> PackagePlugin.PluginContext.Tool
     ) throws -> [Command] {
-        guard let configurationFilePath = sourceFiles.first(
-            where: {
-                $0.path.lastComponent == Self.configurationFileName
-            }
-        )?.path else {
+        guard
+            let configurationFilePath = sourceFiles.first(
+                where: {
+                    $0.path.lastComponent == Self.configurationFileName
+                }
+            )?.path
+        else {
             throw PluginError.noConfigFound(Self.configurationFileName)
         }
         let data = try Data(contentsOf: URL(fileURLWithPath: "\(configurationFilePath)"))
@@ -262,7 +264,7 @@ extension SwiftProtobufPlugin: XcodeBuildToolPlugin {
         context: XcodePluginContext,
         target: XcodeTarget
     ) throws -> [Command] {
-        return try createBuildCommands(
+        try createBuildCommands(
             pluginWorkDirectory: context.pluginWorkDirectory,
             sourceFiles: target.inputFiles,
             tool: context.tool

+ 35 - 33
Sources/Conformance/main.swift

@@ -16,29 +16,29 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-
 import SwiftProtobuf
 
 extension FileHandle {
-  fileprivate func _read(count: Int) -> Data? {
-    if #available(macOS 10.15.4, *) {
-      do {
-        guard let result = try read(upToCount: count),
-              result.count == count else {
-          return nil
+    fileprivate func _read(count: Int) -> Data? {
+        if #available(macOS 10.15.4, *) {
+            do {
+                guard let result = try read(upToCount: count),
+                    result.count == count
+                else {
+                    return nil
+                }
+                return result
+            } catch {
+                return nil
+            }
+        } else {
+            let result = readData(ofLength: count)
+            guard result.count == count else {
+                return nil
+            }
+            return result
         }
-        return result
-      } catch {
-        return nil
-      }
-    } else {
-      let result = readData(ofLength: count)
-      guard result.count == count else {
-        return nil
-      }
-      return result
     }
-  }
 }
 
 func readRequest() -> Data? {
@@ -51,7 +51,8 @@ func readRequest() -> Data? {
     }
     let count = UInt32(littleEndian: countLE)
     guard count < Int.max,
-          let result = stdIn._read(count: Int(count)) else {
+        let result = stdIn._read(count: Int(count))
+    else {
         return nil
     }
     return result
@@ -96,8 +97,8 @@ func buildResponse(serializedData: Data) -> Conformance_ConformanceResponse {
         return response
     case .UNRECOGNIZED(let x):
         response.runtimeError =
-          "ConformanceRequest had a new testCategory (\(x)); regenerate conformance.pb.swift"
-          + " and see what support needs to be added."
+            "ConformanceRequest had a new testCategory (\(x)); regenerate conformance.pb.swift"
+            + " and see what support needs to be added."
         return response
     }
 
@@ -141,9 +142,11 @@ func buildResponse(serializedData: Data) -> Conformance_ConformanceResponse {
         var options = JSONDecodingOptions()
         options.ignoreUnknownFields = (request.testCategory == .jsonIgnoreUnknownParsingTest)
         do {
-            testMessage = try msgType.init(jsonString: json,
-                                           extensions: extensions,
-                                           options: options)
+            testMessage = try msgType.init(
+                jsonString: json,
+                extensions: extensions,
+                options: options
+            )
         } catch let e {
             response.parseError = "JSON failed to parse: \(e)"
             return response
@@ -195,14 +198,14 @@ func buildResponse(serializedData: Data) -> Conformance_ConformanceResponse {
 }
 
 func singleTest() throws -> Bool {
-   if let indata = readRequest() {
-       let response = buildResponse(serializedData: indata)
-       let outdata: Data = try response.serializedData()
-       writeResponse(data: outdata)
-       return true
-   } else {
-       return false
-   }
+    if let indata = readRequest() {
+        let response = buildResponse(serializedData: indata)
+        let outdata: Data = try response.serializedData()
+        writeResponse(data: outdata)
+        return true
+    } else {
+        return false
+    }
 }
 
 Google_Protobuf_Any.register(messageType: ProtobufTestMessages_Proto3_TestAllTypesProto3.self)
@@ -210,4 +213,3 @@ Google_Protobuf_Any.register(messageType: ProtobufTestMessages_Editions_Proto3_T
 
 while try singleTest() {
 }
-

+ 425 - 407
Sources/SwiftProtobuf/AnyMessageStorage.swift

@@ -15,302 +15,317 @@
 
 import Foundation
 
-fileprivate func serializeAnyJSON(
-  for message: any Message,
-  typeURL: String,
-  options: JSONEncodingOptions
+private func serializeAnyJSON(
+    for message: any Message,
+    typeURL: String,
+    options: JSONEncodingOptions
 ) throws -> String {
-  var visitor = try JSONEncodingVisitor(type: type(of: message), options: options)
-  visitor.startObject(message: message)
-  visitor.encodeField(name: "@type", stringValue: typeURL)
-  if let m = message as? (any _CustomJSONCodable) {
-    let value = try m.encodedJSONString(options: options)
-    visitor.encodeField(name: "value", jsonText: value)
-  } else {
-    try message.traverse(visitor: &visitor)
-  }
-  visitor.endObject()
-  return visitor.stringResult
+    var visitor = try JSONEncodingVisitor(type: type(of: message), options: options)
+    visitor.startObject(message: message)
+    visitor.encodeField(name: "@type", stringValue: typeURL)
+    if let m = message as? (any _CustomJSONCodable) {
+        let value = try m.encodedJSONString(options: options)
+        visitor.encodeField(name: "value", jsonText: value)
+    } else {
+        try message.traverse(visitor: &visitor)
+    }
+    visitor.endObject()
+    return visitor.stringResult
 }
 
-fileprivate func emitVerboseTextForm(visitor: inout TextFormatEncodingVisitor, message: any Message, typeURL: String) {
-  let url: String
-  if typeURL.isEmpty {
-    url = buildTypeURL(forMessage: message, typePrefix: defaultAnyTypeURLPrefix)
-  } else {
-    url = typeURL
-  }
-  visitor.visitAnyVerbose(value: message, typeURL: url)
+private func emitVerboseTextForm(visitor: inout TextFormatEncodingVisitor, message: any Message, typeURL: String) {
+    let url: String
+    if typeURL.isEmpty {
+        url = buildTypeURL(forMessage: message, typePrefix: defaultAnyTypeURLPrefix)
+    } else {
+        url = typeURL
+    }
+    visitor.visitAnyVerbose(value: message, typeURL: url)
 }
 
-fileprivate func asJSONObject(body: [UInt8]) -> Data {
-  let asciiOpenCurlyBracket = UInt8(ascii: "{")
-  let asciiCloseCurlyBracket = UInt8(ascii: "}")
-  var result = [asciiOpenCurlyBracket]
-  result.append(contentsOf: body)
-  result.append(asciiCloseCurlyBracket)
-  return Data(result)
+private func asJSONObject(body: [UInt8]) -> Data {
+    let asciiOpenCurlyBracket = UInt8(ascii: "{")
+    let asciiCloseCurlyBracket = UInt8(ascii: "}")
+    var result = [asciiOpenCurlyBracket]
+    result.append(contentsOf: body)
+    result.append(asciiCloseCurlyBracket)
+    return Data(result)
 }
 
-fileprivate func unpack(contentJSON: [UInt8],
-                        extensions: any ExtensionMap,
-                        options: JSONDecodingOptions,
-                        as messageType: any Message.Type) throws -> any Message {
-  guard messageType is any _CustomJSONCodable.Type else {
-    let contentJSONAsObject = asJSONObject(body: contentJSON)
-    return try messageType.init(jsonUTF8Bytes: contentJSONAsObject, extensions: extensions, options: options)
-  }
-
-  var value = String()
-  try contentJSON.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-    if body.count > 0 {
-      // contentJSON will be the valid JSON for inside an object (everything but
-      // the '{' and '}', so minimal validation is needed.
-      var scanner = JSONScanner(source: body, options: options, extensions: extensions)
-      while !scanner.complete {
-        let key = try scanner.nextQuotedString()
-        try scanner.skipRequiredColon()
-        if key == "value" {
-          value = try scanner.skip()
-          break
-        }
-        if !options.ignoreUnknownFields {
-          // The only thing within a WKT should be "value".
-          throw AnyUnpackError.malformedWellKnownTypeJSON
+private func unpack(
+    contentJSON: [UInt8],
+    extensions: any ExtensionMap,
+    options: JSONDecodingOptions,
+    as messageType: any Message.Type
+) throws -> any Message {
+    guard messageType is any _CustomJSONCodable.Type else {
+        let contentJSONAsObject = asJSONObject(body: contentJSON)
+        return try messageType.init(jsonUTF8Bytes: contentJSONAsObject, extensions: extensions, options: options)
+    }
+
+    var value = String()
+    try contentJSON.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+        if body.count > 0 {
+            // contentJSON will be the valid JSON for inside an object (everything but
+            // the '{' and '}', so minimal validation is needed.
+            var scanner = JSONScanner(source: body, options: options, extensions: extensions)
+            while !scanner.complete {
+                let key = try scanner.nextQuotedString()
+                try scanner.skipRequiredColon()
+                if key == "value" {
+                    value = try scanner.skip()
+                    break
+                }
+                if !options.ignoreUnknownFields {
+                    // The only thing within a WKT should be "value".
+                    throw AnyUnpackError.malformedWellKnownTypeJSON
+                }
+                let _ = try scanner.skip()
+                try scanner.skipRequiredComma()
+            }
+            if !options.ignoreUnknownFields && !scanner.complete {
+                // If that wasn't the end, then there was another key, and WKTs should
+                // only have the one when not skipping unknowns.
+                throw AnyUnpackError.malformedWellKnownTypeJSON
+            }
         }
-        let _ = try scanner.skip()
-        try scanner.skipRequiredComma()
-      }
-      if !options.ignoreUnknownFields && !scanner.complete {
-        // If that wasn't the end, then there was another key, and WKTs should
-        // only have the one when not skipping unknowns.
-        throw AnyUnpackError.malformedWellKnownTypeJSON
-      }
     }
-  }
-  return try messageType.init(jsonString: value, extensions: extensions, options: options)
+    return try messageType.init(jsonString: value, extensions: extensions, options: options)
 }
 
 internal class AnyMessageStorage {
-  // The two properties generated Google_Protobuf_Any will reference.
-  var _typeURL = String()
-  var _value: Data {
-    // Remapped to the internal `state`.
-    get {
-      switch state {
-      case .binary(let value):
-        return Data(value)
-      case .message(let message):
-        do {
-          return try message.serializedBytes(partial: true)
-        } catch {
-          return Data()
-        }
-      case .contentJSON(let contentJSON, let options):
-        guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else {
-          return Data()
+    // The two properties generated Google_Protobuf_Any will reference.
+    var _typeURL = String()
+    var _value: Data {
+        // Remapped to the internal `state`.
+        get {
+            switch state {
+            case .binary(let value):
+                return Data(value)
+            case .message(let message):
+                do {
+                    return try message.serializedBytes(partial: true)
+                } catch {
+                    return Data()
+                }
+            case .contentJSON(let contentJSON, let options):
+                guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else {
+                    return Data()
+                }
+                do {
+                    let m = try unpack(
+                        contentJSON: contentJSON,
+                        extensions: SimpleExtensionMap(),
+                        options: options,
+                        as: messageType
+                    )
+                    return try m.serializedBytes(partial: true)
+                } catch {
+                    return Data()
+                }
+            }
         }
-        do {
-          let m = try unpack(contentJSON: contentJSON,
-                             extensions: SimpleExtensionMap(),
-                             options: options,
-                             as: messageType)
-          return try m.serializedBytes(partial: true)
-        } catch {
-          return Data()
+        set {
+            state = .binary(newValue)
         }
-      }
     }
-    set {
-      state = .binary(newValue)
+
+    enum InternalState {
+        // a serialized binary
+        // Note: Unlike contentJSON below, binary does not bother to capture the
+        // decoding options. This is because the actual binary format is the binary
+        // blob, i.e. - when decoding from binary, the spec doesn't include decoding
+        // the binary blob, it is pass through. Instead there is a public api for
+        // unpacking that takes new options when a developer decides to decode it.
+        case binary(Data)
+        // a message
+        case message(any Message)
+        // parsed JSON with the @type removed and the decoding options.
+        case contentJSON([UInt8], JSONDecodingOptions)
     }
-  }
-
-  enum InternalState {
-    // a serialized binary
-    // Note: Unlike contentJSON below, binary does not bother to capture the
-    // decoding options. This is because the actual binary format is the binary
-    // blob, i.e. - when decoding from binary, the spec doesn't include decoding
-    // the binary blob, it is pass through. Instead there is a public api for
-    // unpacking that takes new options when a developer decides to decode it.
-    case binary(Data)
-    // a message
-    case message(any Message)
-    // parsed JSON with the @type removed and the decoding options.
-    case contentJSON([UInt8], JSONDecodingOptions)
-  }
-  var state: InternalState = .binary(Data())
-
-  #if swift(>=5.10)
+    var state: InternalState = .binary(Data())
+
+    #if swift(>=5.10)
     // This property is used as the initial default value for new instances of the type.
     // The type itself is protecting the reference to its storage via CoW semantics.
     // This will force a copy to be made of this reference when the first mutation occurs;
     // hence, it is safe to mark this as `nonisolated(unsafe)`.
     static nonisolated(unsafe) let defaultInstance = AnyMessageStorage()
-  #else
+    #else
     static let defaultInstance = AnyMessageStorage()
-  #endif
-
-  private init() {}
+    #endif
 
-  init(copying source: AnyMessageStorage) {
-    _typeURL = source._typeURL
-    state = source.state
-  }
+    private init() {}
 
-  func isA<M: Message>(_ type: M.Type) -> Bool {
-    if _typeURL.isEmpty {
-      return false
+    init(copying source: AnyMessageStorage) {
+        _typeURL = source._typeURL
+        state = source.state
     }
-    let encodedType = typeName(fromURL: _typeURL)
-    return encodedType == M.protoMessageName
-  }
-
-  // This is only ever called with the expectation that target will be fully
-  // replaced during the unpacking and never as a merge.
-  func unpackTo<M: Message>(
-    target: inout M,
-    extensions: (any ExtensionMap)?,
-    options: BinaryDecodingOptions
-  ) throws {
-    guard isA(M.self) else {
-      throw AnyUnpackError.typeMismatch
+
+    func isA<M: Message>(_ type: M.Type) -> Bool {
+        if _typeURL.isEmpty {
+            return false
+        }
+        let encodedType = typeName(fromURL: _typeURL)
+        return encodedType == M.protoMessageName
     }
 
-    switch state {
-    case .binary(let data):
-      target = try M(serializedBytes: data, extensions: extensions, partial: true, options: options)
-
-    case .message(let msg):
-      if let message = msg as? M {
-        // Already right type, copy it over.
-        target = message
-      } else {
-        // Different type, serialize and parse.
-        let bytes: [UInt8] = try msg.serializedBytes(partial: true)
-        target = try M(serializedBytes: bytes, extensions: extensions, partial: true)
-      }
-
-    case .contentJSON(let contentJSON, let options):
-      target = try unpack(contentJSON: contentJSON,
-                          extensions: extensions ?? SimpleExtensionMap(),
-                          options: options,
-                          as: M.self) as! M
+    // This is only ever called with the expectation that target will be fully
+    // replaced during the unpacking and never as a merge.
+    func unpackTo<M: Message>(
+        target: inout M,
+        extensions: (any ExtensionMap)?,
+        options: BinaryDecodingOptions
+    ) throws {
+        guard isA(M.self) else {
+            throw AnyUnpackError.typeMismatch
+        }
+
+        switch state {
+        case .binary(let data):
+            target = try M(serializedBytes: data, extensions: extensions, partial: true, options: options)
+
+        case .message(let msg):
+            if let message = msg as? M {
+                // Already right type, copy it over.
+                target = message
+            } else {
+                // Different type, serialize and parse.
+                let bytes: [UInt8] = try msg.serializedBytes(partial: true)
+                target = try M(serializedBytes: bytes, extensions: extensions, partial: true)
+            }
+
+        case .contentJSON(let contentJSON, let options):
+            target =
+                try unpack(
+                    contentJSON: contentJSON,
+                    extensions: extensions ?? SimpleExtensionMap(),
+                    options: options,
+                    as: M.self
+                ) as! M
+        }
     }
-  }
-
-  // Called before the message is traversed to do any error preflights.
-  // Since traverse() will use _value, this is our chance to throw
-  // when _value can't.
-  func preTraverse() throws {
-    switch state {
-    case .binary:
-      // Nothing to be checked.
-      break
-
-    case .message:
-      // When set from a developer provided message, partial support
-      // is done. Any message that comes in from another format isn't
-      // checked, and transcoding the isInitialized requirement is
-      // never inserted.
-      break
-
-    case .contentJSON(let contentJSON, let options):
-      // contentJSON requires we have the type available for decoding.
-      guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else {
-        throw BinaryEncodingError.anyTranscodeFailure
-      }
-      do {
-        // Decodes the full JSON and then discard the result.
-        // The regular traversal will decode this again by querying the
-        // `value` field, but that has no way to fail.  As a result,
-        // we need this to accurately handle decode errors.
-        _ = try unpack(contentJSON: contentJSON,
-                       extensions: SimpleExtensionMap(),
-                       options: options,
-                       as: messageType)
-      } catch {
-        throw BinaryEncodingError.anyTranscodeFailure
-      }
+
+    // Called before the message is traversed to do any error preflights.
+    // Since traverse() will use _value, this is our chance to throw
+    // when _value can't.
+    func preTraverse() throws {
+        switch state {
+        case .binary:
+            // Nothing to be checked.
+            break
+
+        case .message:
+            // When set from a developer provided message, partial support
+            // is done. Any message that comes in from another format isn't
+            // checked, and transcoding the isInitialized requirement is
+            // never inserted.
+            break
+
+        case .contentJSON(let contentJSON, let options):
+            // contentJSON requires we have the type available for decoding.
+            guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else {
+                throw BinaryEncodingError.anyTranscodeFailure
+            }
+            do {
+                // Decodes the full JSON and then discard the result.
+                // The regular traversal will decode this again by querying the
+                // `value` field, but that has no way to fail.  As a result,
+                // we need this to accurately handle decode errors.
+                _ = try unpack(
+                    contentJSON: contentJSON,
+                    extensions: SimpleExtensionMap(),
+                    options: options,
+                    as: messageType
+                )
+            } catch {
+                throw BinaryEncodingError.anyTranscodeFailure
+            }
+        }
     }
-  }
 }
 
 /// Custom handling for Text format.
 extension AnyMessageStorage {
-  func decodeTextFormat(typeURL url: String, decoder: inout TextFormatDecoder) throws {
-    // Decoding the verbose form requires knowing the type.
-    _typeURL = url
-    guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: url) else {
-      // The type wasn't registered, can't parse it.
-      throw TextFormatDecodingError.malformedText
-    }
-    let terminator = try decoder.scanner.skipObjectStart()
-    var subDecoder = try TextFormatDecoder(messageType: messageType, scanner: decoder.scanner, terminator: terminator)
-    if messageType == Google_Protobuf_Any.self {
-      var any = Google_Protobuf_Any()
-      try any.decodeTextFormat(decoder: &subDecoder)
-      state = .message(any)
-    } else {
-      var m = messageType.init()
-      try m.decodeMessage(decoder: &subDecoder)
-      state = .message(m)
-    }
-    decoder.scanner = subDecoder.scanner
-    if try decoder.nextFieldNumber() != nil {
-      // Verbose any can never have additional keys.
-      throw TextFormatDecodingError.malformedText
-    }
-  }
-
-  // Specialized traverse for writing out a Text form of the Any.
-  // This prefers the more-legible "verbose" format if it can
-  // use it, otherwise will fall back to simpler forms.
-  internal func textTraverse(visitor: inout TextFormatEncodingVisitor) {
-    switch state {
-    case .binary(let valueData):
-      if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) {
-        // If we can decode it, we can write the readable verbose form:
-        do {
-          let m = try messageType.init(serializedBytes: valueData, partial: true)
-          emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL)
-          return
-        } catch {
-          // Fall through to just print the type and raw binary data.
+    func decodeTextFormat(typeURL url: String, decoder: inout TextFormatDecoder) throws {
+        // Decoding the verbose form requires knowing the type.
+        _typeURL = url
+        guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: url) else {
+            // The type wasn't registered, can't parse it.
+            throw TextFormatDecodingError.malformedText
+        }
+        let terminator = try decoder.scanner.skipObjectStart()
+        var subDecoder = try TextFormatDecoder(
+            messageType: messageType,
+            scanner: decoder.scanner,
+            terminator: terminator
+        )
+        if messageType == Google_Protobuf_Any.self {
+            var any = Google_Protobuf_Any()
+            try any.decodeTextFormat(decoder: &subDecoder)
+            state = .message(any)
+        } else {
+            var m = messageType.init()
+            try m.decodeMessage(decoder: &subDecoder)
+            state = .message(m)
+        }
+        decoder.scanner = subDecoder.scanner
+        if try decoder.nextFieldNumber() != nil {
+            // Verbose any can never have additional keys.
+            throw TextFormatDecodingError.malformedText
         }
-      }
-      if !_typeURL.isEmpty {
-        try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1)
-      }
-      if !valueData.isEmpty {
-        try! visitor.visitSingularBytesField(value: valueData, fieldNumber: 2)
-      }
-
-    case .message(let msg):
-      emitVerboseTextForm(visitor: &visitor, message: msg, typeURL: _typeURL)
-
-    case .contentJSON(let contentJSON, let options):
-      // If we can decode it, we can write the readable verbose form:
-      if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) {
-        do {
-          let m = try unpack(contentJSON: contentJSON,
-                             extensions: SimpleExtensionMap(),
-                             options: options,
-                             as: messageType)
-          emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL)
-          return
-        } catch {
-          // Fall through to just print the raw JSON data
+    }
+
+    // Specialized traverse for writing out a Text form of the Any.
+    // This prefers the more-legible "verbose" format if it can
+    // use it, otherwise will fall back to simpler forms.
+    internal func textTraverse(visitor: inout TextFormatEncodingVisitor) {
+        switch state {
+        case .binary(let valueData):
+            if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) {
+                // If we can decode it, we can write the readable verbose form:
+                do {
+                    let m = try messageType.init(serializedBytes: valueData, partial: true)
+                    emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL)
+                    return
+                } catch {
+                    // Fall through to just print the type and raw binary data.
+                }
+            }
+            if !_typeURL.isEmpty {
+                try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1)
+            }
+            if !valueData.isEmpty {
+                try! visitor.visitSingularBytesField(value: valueData, fieldNumber: 2)
+            }
+
+        case .message(let msg):
+            emitVerboseTextForm(visitor: &visitor, message: msg, typeURL: _typeURL)
+
+        case .contentJSON(let contentJSON, let options):
+            // If we can decode it, we can write the readable verbose form:
+            if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) {
+                do {
+                    let m = try unpack(
+                        contentJSON: contentJSON,
+                        extensions: SimpleExtensionMap(),
+                        options: options,
+                        as: messageType
+                    )
+                    emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL)
+                    return
+                } catch {
+                    // Fall through to just print the raw JSON data
+                }
+            }
+            if !_typeURL.isEmpty {
+                try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1)
+            }
+            // Build a readable form of the JSON:
+            let contentJSONAsObject = asJSONObject(body: contentJSON)
+            visitor.visitAnyJSONBytesField(value: contentJSONAsObject)
         }
-      }
-      if !_typeURL.isEmpty {
-        try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1)
-      }
-      // Build a readable form of the JSON:
-      let contentJSONAsObject = asJSONObject(body: contentJSON)
-      visitor.visitAnyJSONBytesField(value: contentJSONAsObject)
     }
-  }
 }
 
 /// The obvious goal for Hashable/Equatable conformance would be for
@@ -326,165 +341,168 @@ extension AnyMessageStorage {
 /// test.  Of course, regardless of the above, we must guarantee that
 /// hashValue is compatible with equality.
 extension AnyMessageStorage {
-  // Can't use _valueData for a few reasons:
-  // 1. Since decode is done on demand, two objects could be equal
-  //    but created differently (one from JSON, one for Message, etc.),
-  //    and the hash values have to be equal even if we don't have data
-  //    yet.
-  // 2. map<> serialization order is undefined. At the time of writing
-  //    the Swift, Objective-C, and Go runtimes all tend to have random
-  //    orders, so the messages could be identical, but in binary form
-  //    they could differ.
-  public func hash(into hasher: inout Hasher) {
-    if !_typeURL.isEmpty {
-      hasher.combine(_typeURL)
+    // Can't use _valueData for a few reasons:
+    // 1. Since decode is done on demand, two objects could be equal
+    //    but created differently (one from JSON, one for Message, etc.),
+    //    and the hash values have to be equal even if we don't have data
+    //    yet.
+    // 2. map<> serialization order is undefined. At the time of writing
+    //    the Swift, Objective-C, and Go runtimes all tend to have random
+    //    orders, so the messages could be identical, but in binary form
+    //    they could differ.
+    public func hash(into hasher: inout Hasher) {
+        if !_typeURL.isEmpty {
+            hasher.combine(_typeURL)
+        }
     }
-  }
 
-  func isEqualTo(other: AnyMessageStorage) -> Bool {
-    if (_typeURL != other._typeURL) {
-      return false
-    }
+    func isEqualTo(other: AnyMessageStorage) -> Bool {
+        if _typeURL != other._typeURL {
+            return false
+        }
 
-    // Since the library does lazy Any decode, equality is a very hard problem.
-    // It things exactly match, that's pretty easy, otherwise, one ends up having
-    // to error on saying they aren't equal.
-    //
-    // The best option would be to have Message forms and compare those, as that
-    // removes issues like map<> serialization order, some other protocol buffer
-    // implementation details/bugs around serialized form order, etc.; but that
-    // would also greatly slow down equality tests.
-    //
-    // Do our best to compare what is present have...
-
-    // If both have messages, check if they are the same.
-    if case .message(let myMsg) = state, case .message(let otherMsg) = other.state, type(of: myMsg) == type(of: otherMsg) {
-      // Since the messages are known to be same type, we can claim both equal and
-      // not equal based on the equality comparison.
-      return myMsg.isEqualTo(message: otherMsg)
-    }
+        // Since the library does lazy Any decode, equality is a very hard problem.
+        // It things exactly match, that's pretty easy, otherwise, one ends up having
+        // to error on saying they aren't equal.
+        //
+        // The best option would be to have Message forms and compare those, as that
+        // removes issues like map<> serialization order, some other protocol buffer
+        // implementation details/bugs around serialized form order, etc.; but that
+        // would also greatly slow down equality tests.
+        //
+        // Do our best to compare what is present have...
+
+        // If both have messages, check if they are the same.
+        if case .message(let myMsg) = state, case .message(let otherMsg) = other.state,
+            type(of: myMsg) == type(of: otherMsg)
+        {
+            // Since the messages are known to be same type, we can claim both equal and
+            // not equal based on the equality comparison.
+            return myMsg.isEqualTo(message: otherMsg)
+        }
 
-    // If both have serialized data, and they exactly match; the messages are equal.
-    // Because there could be map in the message, the fact that the data isn't the
-    // same doesn't always mean the messages aren't equal. Likewise, the binary could
-    // have been created by a library that doesn't order the fields, or the binary was
-    // created using the appending ability in of the binary format.
-    if case .binary(let myValue) = state, case .binary(let otherValue) = other.state, myValue == otherValue {
-      return true
-    }
+        // If both have serialized data, and they exactly match; the messages are equal.
+        // Because there could be map in the message, the fact that the data isn't the
+        // same doesn't always mean the messages aren't equal. Likewise, the binary could
+        // have been created by a library that doesn't order the fields, or the binary was
+        // created using the appending ability in of the binary format.
+        if case .binary(let myValue) = state, case .binary(let otherValue) = other.state, myValue == otherValue {
+            return true
+        }
 
-    // If both have contentJSON, and they exactly match; the messages are equal.
-    // Because there could be map in the message (or the JSON could just be in a different
-    // order), the fact that the JSON isn't the same doesn't always mean the messages
-    // aren't equal.
-    if case .contentJSON(let myJSON, _) = state,
-       case .contentJSON(let otherJSON, _) = other.state,
-       myJSON == otherJSON {
-      return true
-    }
+        // If both have contentJSON, and they exactly match; the messages are equal.
+        // Because there could be map in the message (or the JSON could just be in a different
+        // order), the fact that the JSON isn't the same doesn't always mean the messages
+        // aren't equal.
+        if case .contentJSON(let myJSON, _) = state,
+            case .contentJSON(let otherJSON, _) = other.state,
+            myJSON == otherJSON
+        {
+            return true
+        }
 
-    // Out of options. To do more compares, the states conversions would have to be
-    // done to do comparisons; and since equality can be used somewhat removed from
-    // a developer (if they put protos in a Set, use them as keys to a Dictionary, etc),
-    // the conversion cost might be to high for those uses.  Give up and say they aren't equal.
-    return false
-  }
+        // Out of options. To do more compares, the states conversions would have to be
+        // done to do comparisons; and since equality can be used somewhat removed from
+        // a developer (if they put protos in a Set, use them as keys to a Dictionary, etc),
+        // the conversion cost might be to high for those uses.  Give up and say they aren't equal.
+        return false
+    }
 }
 
 // _CustomJSONCodable support for Google_Protobuf_Any
 extension AnyMessageStorage {
-  // Override the traversal-based JSON encoding
-  // This builds an Any JSON representation from one of:
-  //  * The message we were initialized with,
-  //  * The JSON fields we last deserialized, or
-  //  * The protobuf field we were deserialized from.
-  // The last case requires locating the type, deserializing
-  // into an object, then reserializing back to JSON.
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    switch state {
-    case .binary(let valueData):
-      // Follow the C++ protostream_objectsource.cc's
-      // ProtoStreamObjectSource::RenderAny() special casing of an empty value.
-      guard !valueData.isEmpty else {
-        if _typeURL.isEmpty {
-          return "{}"
+    // Override the traversal-based JSON encoding
+    // This builds an Any JSON representation from one of:
+    //  * The message we were initialized with,
+    //  * The JSON fields we last deserialized, or
+    //  * The protobuf field we were deserialized from.
+    // The last case requires locating the type, deserializing
+    // into an object, then reserializing back to JSON.
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        switch state {
+        case .binary(let valueData):
+            // Follow the C++ protostream_objectsource.cc's
+            // ProtoStreamObjectSource::RenderAny() special casing of an empty value.
+            guard !valueData.isEmpty else {
+                if _typeURL.isEmpty {
+                    return "{}"
+                }
+                var jsonEncoder = JSONEncoder()
+                jsonEncoder.startField(name: "@type")
+                jsonEncoder.putStringValue(value: _typeURL)
+                jsonEncoder.endObject()
+                return jsonEncoder.stringResult
+            }
+            // Transcode by decoding the binary data to a message object
+            // and then recode back into JSON.
+            guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else {
+                // If we don't have the type available, we can't decode the
+                // binary value, so we're stuck.  (The Google spec does not
+                // provide a way to just package the binary value for someone
+                // else to decode later.)
+                throw JSONEncodingError.anyTranscodeFailure
+            }
+            let m = try messageType.init(serializedBytes: valueData, partial: true)
+            return try serializeAnyJSON(for: m, typeURL: _typeURL, options: options)
+
+        case .message(let msg):
+            // We should have been initialized with a typeURL, but
+            // ensure it wasn't cleared.
+            let url = !_typeURL.isEmpty ? _typeURL : buildTypeURL(forMessage: msg, typePrefix: defaultAnyTypeURLPrefix)
+            return try serializeAnyJSON(for: msg, typeURL: url, options: options)
+
+        case .contentJSON(let contentJSON, _):
+            var jsonEncoder = JSONEncoder()
+            jsonEncoder.startObject()
+            jsonEncoder.startField(name: "@type")
+            jsonEncoder.putStringValue(value: _typeURL)
+            if !contentJSON.isEmpty {
+                jsonEncoder.append(staticText: ",")
+                // NOTE: This doesn't really take `options` into account since it is
+                // just reflecting out what was taken in originally.
+                jsonEncoder.append(utf8Bytes: contentJSON)
+            }
+            jsonEncoder.endObject()
+            return jsonEncoder.stringResult
         }
-        var jsonEncoder = JSONEncoder()
-        jsonEncoder.startField(name: "@type")
-        jsonEncoder.putStringValue(value: _typeURL)
-        jsonEncoder.endObject()
-        return jsonEncoder.stringResult
-      }
-      // Transcode by decoding the binary data to a message object
-      // and then recode back into JSON.
-      guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else {
-        // If we don't have the type available, we can't decode the
-        // binary value, so we're stuck.  (The Google spec does not
-        // provide a way to just package the binary value for someone
-        // else to decode later.)
-        throw JSONEncodingError.anyTranscodeFailure
-      }
-      let m = try messageType.init(serializedBytes: valueData, partial: true)
-      return try serializeAnyJSON(for: m, typeURL: _typeURL, options: options)
-
-    case .message(let msg):
-      // We should have been initialized with a typeURL, but
-      // ensure it wasn't cleared.
-      let url = !_typeURL.isEmpty ? _typeURL : buildTypeURL(forMessage: msg, typePrefix: defaultAnyTypeURLPrefix)
-      return try serializeAnyJSON(for: msg, typeURL: url, options: options)
-
-    case .contentJSON(let contentJSON, _):
-      var jsonEncoder = JSONEncoder()
-      jsonEncoder.startObject()
-      jsonEncoder.startField(name: "@type")
-      jsonEncoder.putStringValue(value: _typeURL)
-      if !contentJSON.isEmpty {
-        jsonEncoder.append(staticText: ",")
-        // NOTE: This doesn't really take `options` into account since it is
-        // just reflecting out what was taken in originally.
-        jsonEncoder.append(utf8Bytes: contentJSON)
-      }
-      jsonEncoder.endObject()
-      return jsonEncoder.stringResult
-    }
-  }
-
-  // TODO: If the type is well-known or has already been registered,
-  // we should consider decoding eagerly.  Eager decoding would
-  // catch certain errors earlier (good) but would probably be
-  // a performance hit if the Any contents were never accessed (bad).
-  // Of course, we can't always decode eagerly (we don't always have the
-  // message type available), so the deferred logic here is still needed.
-  func decodeJSON(from decoder: inout JSONDecoder) throws {
-    try decoder.scanner.skipRequiredObjectStart()
-    // Reset state
-    _typeURL = String()
-    state = .binary(Data())
-    if decoder.scanner.skipOptionalObjectEnd() {
-      return
     }
 
-    var jsonEncoder = JSONEncoder()
-    while true {
-      let key = try decoder.scanner.nextQuotedString()
-      try decoder.scanner.skipRequiredColon()
-      if key == "@type" {
-        _typeURL = try decoder.scanner.nextQuotedString()
-      } else {
-        jsonEncoder.startField(name: key)
-        let keyValueJSON = try decoder.scanner.skip()
-        jsonEncoder.append(text: keyValueJSON)
-      }
-      if decoder.scanner.skipOptionalObjectEnd() {
-        // Capture the options, but set the messageDepthLimit to be what
-        // was left right now, as that is the limit when the JSON is finally
-        // parsed.
-        var updatedOptions = decoder.options
-        updatedOptions.messageDepthLimit = decoder.scanner.recursionBudget
-        state = .contentJSON(Array(jsonEncoder.dataResult), updatedOptions)
-        return
-      }
-      try decoder.scanner.skipRequiredComma()
+    // TODO: If the type is well-known or has already been registered,
+    // we should consider decoding eagerly.  Eager decoding would
+    // catch certain errors earlier (good) but would probably be
+    // a performance hit if the Any contents were never accessed (bad).
+    // Of course, we can't always decode eagerly (we don't always have the
+    // message type available), so the deferred logic here is still needed.
+    func decodeJSON(from decoder: inout JSONDecoder) throws {
+        try decoder.scanner.skipRequiredObjectStart()
+        // Reset state
+        _typeURL = String()
+        state = .binary(Data())
+        if decoder.scanner.skipOptionalObjectEnd() {
+            return
+        }
+
+        var jsonEncoder = JSONEncoder()
+        while true {
+            let key = try decoder.scanner.nextQuotedString()
+            try decoder.scanner.skipRequiredColon()
+            if key == "@type" {
+                _typeURL = try decoder.scanner.nextQuotedString()
+            } else {
+                jsonEncoder.startField(name: key)
+                let keyValueJSON = try decoder.scanner.skip()
+                jsonEncoder.append(text: keyValueJSON)
+            }
+            if decoder.scanner.skipOptionalObjectEnd() {
+                // Capture the options, but set the messageDepthLimit to be what
+                // was left right now, as that is the limit when the JSON is finally
+                // parsed.
+                var updatedOptions = decoder.options
+                updatedOptions.messageDepthLimit = decoder.scanner.recursionBudget
+                state = .contentJSON(Array(jsonEncoder.dataResult), updatedOptions)
+                return
+            }
+            try decoder.scanner.skipRequiredComma()
+        }
     }
-  }
 }

+ 10 - 10
Sources/SwiftProtobuf/AnyUnpackError.swift

@@ -22,16 +22,16 @@
 /// regular decoding operation.  There are also other errors that can occur due
 /// to problems with the `Any` value's structure.
 public enum AnyUnpackError: Error {
-  /// The `type_url` field in the `Google_Protobuf_Any` message did not match
-  /// the message type provided to the `unpack()` method.
-  case typeMismatch
+    /// The `type_url` field in the `Google_Protobuf_Any` message did not match
+    /// the message type provided to the `unpack()` method.
+    case typeMismatch
 
-  /// Well-known types being decoded from JSON must have only two fields: the
-  /// `@type` field and a `value` field containing the specialized JSON coding
-  /// of the well-known type.
-  case malformedWellKnownTypeJSON
+    /// Well-known types being decoded from JSON must have only two fields: the
+    /// `@type` field and a `value` field containing the specialized JSON coding
+    /// of the well-known type.
+    case malformedWellKnownTypeJSON
 
-  /// The `Google_Protobuf_Any` message was malformed in some other way not
-  /// covered by the other error cases.
-  case malformedAnyField
+    /// The `Google_Protobuf_Any` message was malformed in some other way not
+    /// covered by the other error cases.
+    case malformedAnyField
 }

+ 172 - 172
Sources/SwiftProtobuf/AsyncMessageSequence.swift

@@ -15,203 +15,203 @@
 
 @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
 extension AsyncSequence where Element == UInt8 {
-  /// Creates an asynchronous sequence of size-delimited messages from this sequence of bytes.
-  /// Delimited format allows a single file or stream to contain multiple messages. A delimited message
-  /// is a varint encoding the message size followed by a message of exactly that size.
-  ///
-  /// - Parameters:
-  ///   - messageType: The type of message to read.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any extensions in
-  ///    messages encoded by this sequence, or in messages nested within these messages.
-  ///   - partial: If `false` (the default),  after decoding a message, ``Message/isInitialized-6abgi`
-  ///     will be checked to ensure all fields are present.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes.
-  @inlinable
-  public func binaryProtobufDelimitedMessages<M: Message>(
-    of messageType: M.Type = M.self,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) -> AsyncMessageSequence<Self, M> {
-    AsyncMessageSequence<Self, M>(
-      base: self,
-      extensions: extensions,
-      partial: partial,
-      options: options
-    )
-  }
+    /// Creates an asynchronous sequence of size-delimited messages from this sequence of bytes.
+    /// Delimited format allows a single file or stream to contain multiple messages. A delimited message
+    /// is a varint encoding the message size followed by a message of exactly that size.
+    ///
+    /// - Parameters:
+    ///   - messageType: The type of message to read.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any extensions in
+    ///    messages encoded by this sequence, or in messages nested within these messages.
+    ///   - partial: If `false` (the default),  after decoding a message, ``Message/isInitialized-6abgi`
+    ///     will be checked to ensure all fields are present.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes.
+    @inlinable
+    public func binaryProtobufDelimitedMessages<M: Message>(
+        of messageType: M.Type = M.self,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) -> AsyncMessageSequence<Self, M> {
+        AsyncMessageSequence<Self, M>(
+            base: self,
+            extensions: extensions,
+            partial: partial,
+            options: options
+        )
+    }
 }
 
 /// An asynchronous sequence of messages decoded from an asynchronous sequence of bytes.
 @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
 public struct AsyncMessageSequence<
-  Base: AsyncSequence,
-  M: Message
+    Base: AsyncSequence,
+    M: Message
 >: AsyncSequence where Base.Element == UInt8 {
 
-  /// The message type in this asynchronous sequence.
-  public typealias Element = M
+    /// The message type in this asynchronous sequence.
+    public typealias Element = M
 
-  private let base: Base
-  private let extensions: (any ExtensionMap)?
-  private let partial: Bool
-  private let options: BinaryDecodingOptions
+    private let base: Base
+    private let extensions: (any ExtensionMap)?
+    private let partial: Bool
+    private let options: BinaryDecodingOptions
 
-  /// Reads size-delimited messages from the given sequence of bytes. Delimited
-  /// format allows a single file or stream to contain multiple messages. A delimited message
-  /// is a varint encoding the message size followed by a message of exactly that size.
-  ///
-  /// - Parameters:
-  ///   - baseSequence: The `AsyncSequence` to read messages from.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any extensions in
-  ///    messages encoded by this sequence, or in messages nested within these messages.
-  ///   - partial: If `false` (the default), after decoding a message, ``Message/isInitialized-6abgi``
-  ///     will be checked to ensure all fields are present.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes.
-  public init(
-    base: Base,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) {
-    self.base = base
-    self.extensions = extensions
-    self.partial = partial
-    self.options = options
-  }
-
-  /// An asynchronous iterator that produces the messages of this asynchronous sequence.
-  public struct AsyncIterator: AsyncIteratorProtocol {
-    @usableFromInline
-    var iterator: Base.AsyncIterator?
-    @usableFromInline
-    let extensions: (any ExtensionMap)?
-    @usableFromInline
-    let partial: Bool
-    @usableFromInline
-    let options: BinaryDecodingOptions
-
-    init(
-      iterator: Base.AsyncIterator,
-      extensions: (any ExtensionMap)?,
-      partial: Bool,
-      options: BinaryDecodingOptions
+    /// Reads size-delimited messages from the given sequence of bytes. Delimited
+    /// format allows a single file or stream to contain multiple messages. A delimited message
+    /// is a varint encoding the message size followed by a message of exactly that size.
+    ///
+    /// - Parameters:
+    ///   - baseSequence: The `AsyncSequence` to read messages from.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any extensions in
+    ///    messages encoded by this sequence, or in messages nested within these messages.
+    ///   - partial: If `false` (the default), after decoding a message, ``Message/isInitialized-6abgi``
+    ///     will be checked to ensure all fields are present.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes.
+    public init(
+        base: Base,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
     ) {
-      self.iterator = iterator
-      self.extensions = extensions
-      self.partial = partial
-      self.options = options
+        self.base = base
+        self.extensions = extensions
+        self.partial = partial
+        self.options = options
     }
 
-    /// Asynchronously reads the next varint.
-    @inlinable
-    mutating func nextVarInt() async throws -> UInt64? {
-      var messageSize: UInt64 = 0
-      var shift: UInt64 = 0
+    /// An asynchronous iterator that produces the messages of this asynchronous sequence.
+    public struct AsyncIterator: AsyncIteratorProtocol {
+        @usableFromInline
+        var iterator: Base.AsyncIterator?
+        @usableFromInline
+        let extensions: (any ExtensionMap)?
+        @usableFromInline
+        let partial: Bool
+        @usableFromInline
+        let options: BinaryDecodingOptions
 
-      while let byte = try await iterator?.next() {
-        messageSize |= UInt64(byte & 0x7f) << shift
-        shift += UInt64(7)
-        if shift > 35 {
-          iterator = nil
-          throw SwiftProtobufError.BinaryStreamDecoding.malformedLength()
+        init(
+            iterator: Base.AsyncIterator,
+            extensions: (any ExtensionMap)?,
+            partial: Bool,
+            options: BinaryDecodingOptions
+        ) {
+            self.iterator = iterator
+            self.extensions = extensions
+            self.partial = partial
+            self.options = options
         }
-        if (byte & 0x80 == 0) {
-          return messageSize
+
+        /// Asynchronously reads the next varint.
+        @inlinable
+        mutating func nextVarInt() async throws -> UInt64? {
+            var messageSize: UInt64 = 0
+            var shift: UInt64 = 0
+
+            while let byte = try await iterator?.next() {
+                messageSize |= UInt64(byte & 0x7f) << shift
+                shift += UInt64(7)
+                if shift > 35 {
+                    iterator = nil
+                    throw SwiftProtobufError.BinaryStreamDecoding.malformedLength()
+                }
+                if byte & 0x80 == 0 {
+                    return messageSize
+                }
+            }
+            if shift > 0 {
+                // The stream has ended inside a varint.
+                iterator = nil
+                throw BinaryDelimited.Error.truncated
+            }
+            return nil  // End of stream reached.
         }
-      }
-      if (shift > 0) {
-        // The stream has ended inside a varint.
-        iterator = nil
-        throw BinaryDelimited.Error.truncated
-      }
-      return nil // End of stream reached.
-    }
 
-    /// Helper to read the given number of bytes.
-    @usableFromInline
-    mutating func readBytes(_ size: Int) async throws -> [UInt8] {
-      // Even though the bytes are read in chunks, things can still hard fail if
-      // there isn't enough memory to append to have all the bytes at once for
-      // parsing; but this at least catches some possible OOM attacks.
-      var bytesNeeded = size
-      var buffer = [UInt8]()
-      let kChunkSize = 16 * 1024 * 1024
-      var chunk = [UInt8](repeating: 0, count: Swift.min(bytesNeeded, kChunkSize))
-      while bytesNeeded > 0 {
-        var consumedBytes = 0
-        let maxLength = Swift.min(bytesNeeded, chunk.count)
-        while consumedBytes < maxLength {
-          guard let byte = try await iterator?.next() else {
-            // The iterator hit the end, but the chunk wasn't filled, so the full
-            // payload wasn't read.
-            throw BinaryDelimited.Error.truncated
-          }
-          chunk[consumedBytes] = byte
-          consumedBytes += 1
+        /// Helper to read the given number of bytes.
+        @usableFromInline
+        mutating func readBytes(_ size: Int) async throws -> [UInt8] {
+            // Even though the bytes are read in chunks, things can still hard fail if
+            // there isn't enough memory to append to have all the bytes at once for
+            // parsing; but this at least catches some possible OOM attacks.
+            var bytesNeeded = size
+            var buffer = [UInt8]()
+            let kChunkSize = 16 * 1024 * 1024
+            var chunk = [UInt8](repeating: 0, count: Swift.min(bytesNeeded, kChunkSize))
+            while bytesNeeded > 0 {
+                var consumedBytes = 0
+                let maxLength = Swift.min(bytesNeeded, chunk.count)
+                while consumedBytes < maxLength {
+                    guard let byte = try await iterator?.next() else {
+                        // The iterator hit the end, but the chunk wasn't filled, so the full
+                        // payload wasn't read.
+                        throw BinaryDelimited.Error.truncated
+                    }
+                    chunk[consumedBytes] = byte
+                    consumedBytes += 1
+                }
+                if consumedBytes < chunk.count {
+                    buffer += chunk[0..<consumedBytes]
+                } else {
+                    buffer += chunk
+                }
+                bytesNeeded -= maxLength
+            }
+            return buffer
         }
-        if consumedBytes < chunk.count {
-          buffer += chunk[0..<consumedBytes]
-        } else {
-          buffer += chunk
+
+        /// Asynchronously advances to the next message and returns it, or ends the
+        /// sequence if there is no next message.
+        ///
+        /// - Returns: The next message, if it exists, or `nil` to signal the end of
+        ///   the sequence.
+        @inlinable
+        public mutating func next() async throws -> M? {
+            guard let messageSize = try await nextVarInt() else {
+                iterator = nil
+                return nil
+            }
+            guard messageSize <= UInt64(0x7fff_ffff) else {
+                iterator = nil
+                throw SwiftProtobufError.BinaryDecoding.tooLarge()
+            }
+            if messageSize == 0 {
+                return try M(
+                    serializedBytes: [],
+                    extensions: extensions,
+                    partial: partial,
+                    options: options
+                )
+            }
+            let buffer = try await readBytes(Int(messageSize))
+            return try M(
+                serializedBytes: buffer,
+                extensions: extensions,
+                partial: partial,
+                options: options
+            )
         }
-        bytesNeeded -= maxLength
-      }
-      return buffer
     }
 
-    /// Asynchronously advances to the next message and returns it, or ends the
-    /// sequence if there is no next message.
+    /// Creates the asynchronous iterator that produces elements of this
+    /// asynchronous sequence.
     ///
-    /// - Returns: The next message, if it exists, or `nil` to signal the end of
-    ///   the sequence.
-    @inlinable
-    public mutating func next() async throws -> M? {
-      guard let messageSize = try await nextVarInt() else {
-        iterator = nil
-        return nil
-      }
-      guard messageSize <= UInt64(0x7fffffff) else {
-        iterator = nil
-        throw SwiftProtobufError.BinaryDecoding.tooLarge()
-      }
-      if messageSize == 0 {
-        return try M(
-          serializedBytes: [],
-          extensions: extensions,
-          partial: partial,
-          options: options
+    /// - Returns: An instance of the `AsyncIterator` type used to produce
+    /// messages in the asynchronous sequence.
+    public func makeAsyncIterator() -> AsyncMessageSequence.AsyncIterator {
+        AsyncIterator(
+            iterator: base.makeAsyncIterator(),
+            extensions: extensions,
+            partial: partial,
+            options: options
         )
-      }
-      let buffer = try await readBytes(Int(messageSize))
-      return try M(
-        serializedBytes: buffer,
-        extensions: extensions,
-        partial: partial,
-        options: options
-      )
     }
-  }
-
-  /// Creates the asynchronous iterator that produces elements of this
-  /// asynchronous sequence.
-  ///
-  /// - Returns: An instance of the `AsyncIterator` type used to produce
-  /// messages in the asynchronous sequence.
-  public func makeAsyncIterator() -> AsyncMessageSequence.AsyncIterator {
-    AsyncIterator(
-      iterator: base.makeAsyncIterator(),
-      extensions: extensions,
-      partial: partial,
-      options: options
-    )
-  }
 }
 
 @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
-extension AsyncMessageSequence: Sendable where Base: Sendable { }
+extension AsyncMessageSequence: Sendable where Base: Sendable {}
 
 @available(*, unavailable)
-extension AsyncMessageSequence.AsyncIterator: Sendable { }
+extension AsyncMessageSequence.AsyncIterator: Sendable {}

+ 123 - 101
Sources/SwiftProtobuf/BinaryDecoder.swift

@@ -19,13 +19,13 @@ import Foundation
 
 internal struct BinaryDecoder: Decoder {
     // Current position
-    private var p : UnsafeRawPointer
+    private var p: UnsafeRawPointer
     // Remaining bytes in input.
-    private var available : Int
+    private var available: Int
     // Position of start of field currently being parsed
-    private var fieldStartP : UnsafeRawPointer
+    private var fieldStartP: UnsafeRawPointer
     // Position of end of field currently being parsed, nil if we don't know.
-    private var fieldEndP : UnsafeRawPointer?
+    private var fieldEndP: UnsafeRawPointer?
     // Whether or not the field value  has actually been parsed
     private var consumed = true
     // Wire format for last-examined field
@@ -48,13 +48,13 @@ internal struct BinaryDecoder: Decoder {
     // packed repeated enums; see below
     private var unknownOverride: Data?
 
-    private var complete: Bool {return available == 0}
+    private var complete: Bool { available == 0 }
 
     internal init(
-      forReadingFrom pointer: UnsafeRawPointer,
-      count: Int,
-      options: BinaryDecodingOptions,
-      extensions: (any ExtensionMap)? = nil
+        forReadingFrom pointer: UnsafeRawPointer,
+        count: Int,
+        options: BinaryDecodingOptions,
+        extensions: (any ExtensionMap)? = nil
     ) {
         // Assuming baseAddress is not nil.
         p = pointer
@@ -66,15 +66,17 @@ internal struct BinaryDecoder: Decoder {
     }
 
     internal init(
-      forReadingFrom pointer: UnsafeRawPointer,
-      count: Int,
-      parent: BinaryDecoder
+        forReadingFrom pointer: UnsafeRawPointer,
+        count: Int,
+        parent: BinaryDecoder
     ) {
-      self.init(forReadingFrom: pointer,
-                count: count,
-                options: parent.options,
-                extensions: parent.extensions)
-      recursionBudget = parent.recursionBudget
+        self.init(
+            forReadingFrom: pointer,
+            count: count,
+            options: parent.options,
+            extensions: parent.extensions
+        )
+        recursionBudget = parent.recursionBudget
     }
 
     private mutating func incrementRecursionDepth() throws {
@@ -789,18 +791,18 @@ internal struct BinaryDecoder: Decoder {
 
     internal mutating func decodeSingularEnumField<E: Enum>(value: inout E?) throws where E.RawValue == Int {
         guard fieldWireFormat == WireFormat.varint else {
-             return
-         }
+            return
+        }
         let varint = try decodeVarint()
         if let v = E(rawValue: Int(Int32(truncatingIfNeeded: varint))) {
             value = v
             consumed = true
         }
-     }
+    }
 
     internal mutating func decodeSingularEnumField<E: Enum>(value: inout E) throws where E.RawValue == Int {
         guard fieldWireFormat == WireFormat.varint else {
-             return
+            return
         }
         let varint = try decodeVarint()
         if let v = E(rawValue: Int(Int32(truncatingIfNeeded: varint))) {
@@ -839,15 +841,16 @@ internal struct BinaryDecoder: Decoder {
             if let extras = extras {
                 let fieldTag = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
                 let bodySize = extras.reduce(0) { $0 + Varint.encodedSize(of: Int64($1)) }
-                let fieldSize = Varint.encodedSize(of: fieldTag.rawValue) + Varint.encodedSize(of: Int64(bodySize)) + bodySize
+                let fieldSize =
+                    Varint.encodedSize(of: fieldTag.rawValue) + Varint.encodedSize(of: Int64(bodySize)) + bodySize
                 var field = Data(count: fieldSize)
                 field.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
-                  var encoder = BinaryEncoder(forWritingInto: body)
-                  encoder.startField(tag: fieldTag)
-                  encoder.putVarInt(value: Int64(bodySize))
-                  for v in extras {
-                      encoder.putVarInt(value: Int64(v))
-                  }
+                    var encoder = BinaryEncoder(forWritingInto: body)
+                    encoder.startField(tag: fieldTag)
+                    encoder.putVarInt(value: Int64(bodySize))
+                    for v in extras {
+                        encoder.putVarInt(value: Int64(v))
+                    }
                 }
                 unknownOverride = field
             }
@@ -885,16 +888,16 @@ internal struct BinaryDecoder: Decoder {
     }
 
     internal mutating func decodeFullMessage<M: Message>(message: inout M) throws {
-      assert(unknownData == nil)
-      try incrementRecursionDepth()
-      try message.decodeMessage(decoder: &self)
-      decrementRecursionDepth()
-      guard complete else {
-        throw BinaryDecodingError.trailingGarbage
-      }
-      if let unknownData = unknownData {
-        message.unknownFields.append(protobufData: unknownData)
-      }
+        assert(unknownData == nil)
+        try incrementRecursionDepth()
+        try message.decodeMessage(decoder: &self)
+        decrementRecursionDepth()
+        guard complete else {
+            throw BinaryDecodingError.trailingGarbage
+        }
+        if let unknownData = unknownData {
+            message.unknownFields.append(protobufData: unknownData)
+        }
     }
 
     internal mutating func decodeSingularGroupField<G: Message>(value: inout G?) throws {
@@ -947,7 +950,10 @@ internal struct BinaryDecoder: Decoder {
         return true
     }
 
-    internal mutating func decodeMapField<KeyType, ValueType: MapValueType>(fieldType: _ProtobufMap<KeyType, ValueType>.Type, value: inout _ProtobufMap<KeyType, ValueType>.BaseType) throws {
+    internal mutating func decodeMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMap<KeyType, ValueType>.BaseType
+    ) throws {
         guard fieldWireFormat == WireFormat.lengthDelimited else {
             return
         }
@@ -966,7 +972,7 @@ internal struct BinaryDecoder: Decoder {
                 try KeyType.decodeSingular(value: &k, from: &subdecoder)
             case 2:
                 try ValueType.decodeSingular(value: &v, from: &subdecoder)
-            default: // Skip any other fields within the map entry object
+            default:  // Skip any other fields within the map entry object
                 try subdecoder.skip()
             }
         }
@@ -982,7 +988,10 @@ internal struct BinaryDecoder: Decoder {
         consumed = true
     }
 
-    internal mutating func decodeMapField<KeyType, ValueType>(fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type, value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType) throws where ValueType.RawValue == Int {
+    internal mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
+    ) throws where ValueType.RawValue == Int {
         guard fieldWireFormat == WireFormat.lengthDelimited else {
             return
         }
@@ -997,9 +1006,9 @@ internal struct BinaryDecoder: Decoder {
             }
             let fieldNumber = tag.fieldNumber
             switch fieldNumber {
-            case 1: // Keys are basic types
+            case 1:  // Keys are basic types
                 try KeyType.decodeSingular(value: &k, from: &subdecoder)
-            case 2: // Value is an Enum type
+            case 2:  // Value is an Enum type
                 try subdecoder.decodeSingularEnumField(value: &v)
                 if v == nil && tag.wireFormat == .varint {
                     // Enum decode fail and wire format was varint, so this had to
@@ -1009,7 +1018,7 @@ internal struct BinaryDecoder: Decoder {
                     // the map entry.
                     return
                 }
-            default: // Skip any other fields within the map entry object
+            default:  // Skip any other fields within the map entry object
                 try subdecoder.skip()
             }
         }
@@ -1022,7 +1031,10 @@ internal struct BinaryDecoder: Decoder {
         consumed = true
     }
 
-    internal mutating func decodeMapField<KeyType, ValueType>(fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type, value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType) throws {
+    internal mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
+    ) throws {
         guard fieldWireFormat == WireFormat.lengthDelimited else {
             return
         }
@@ -1037,11 +1049,11 @@ internal struct BinaryDecoder: Decoder {
             }
             let fieldNumber = tag.fieldNumber
             switch fieldNumber {
-            case 1: // Keys are basic types
+            case 1:  // Keys are basic types
                 try KeyType.decodeSingular(value: &k, from: &subdecoder)
-            case 2: // Value is a message type
+            case 2:  // Value is a message type
                 try subdecoder.decodeSingularMessageField(value: &v)
-            default: // Skip any other fields within the map entry object
+            default:  // Skip any other fields within the map entry object
                 try subdecoder.skip()
             }
         }
@@ -1055,24 +1067,26 @@ internal struct BinaryDecoder: Decoder {
     }
 
     internal mutating func decodeExtensionField(
-      values: inout ExtensionFieldValueSet,
-      messageType: any Message.Type,
-      fieldNumber: Int
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type,
+        fieldNumber: Int
     ) throws {
         if let ext = extensions?[messageType, fieldNumber] {
-            try decodeExtensionField(values: &values,
-                                     messageType: messageType,
-                                     fieldNumber: fieldNumber,
-                                     messageExtension: ext)
+            try decodeExtensionField(
+                values: &values,
+                messageType: messageType,
+                fieldNumber: fieldNumber,
+                messageExtension: ext
+            )
         }
     }
 
     /// Helper to reuse between Extension decoding and MessageSet Extension decoding.
     private mutating func decodeExtensionField(
-      values: inout ExtensionFieldValueSet,
-      messageType: any Message.Type,
-      fieldNumber: Int,
-      messageExtension ext: any AnyMessageExtension
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type,
+        fieldNumber: Int,
+        messageExtension ext: any AnyMessageExtension
     ) throws {
         assert(!consumed)
         assert(fieldNumber == ext.fieldNumber)
@@ -1096,13 +1110,13 @@ internal struct BinaryDecoder: Decoder {
     }
 
     internal mutating func decodeExtensionFieldsAsMessageSet(
-      values: inout ExtensionFieldValueSet,
-      messageType: any Message.Type
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type
     ) throws {
         // Spin looking for the Item group, everything else will end up in unknown fields.
         while let fieldNumber = try self.nextFieldNumber() {
-            guard fieldNumber == WireFormat.MessageSet.FieldNumbers.item &&
-              fieldWireFormat == WireFormat.startGroup else {
+            guard fieldNumber == WireFormat.MessageSet.FieldNumbers.item && fieldWireFormat == WireFormat.startGroup
+            else {
                 continue
             }
 
@@ -1113,19 +1127,21 @@ internal struct BinaryDecoder: Decoder {
             subDecoder.groupFieldNumber = fieldNumber
             subDecoder.consumed = true
 
-            let itemResult = try subDecoder.decodeMessageSetItem(values: &values,
-                                                                 messageType: messageType)
+            let itemResult = try subDecoder.decodeMessageSetItem(
+                values: &values,
+                messageType: messageType
+            )
             switch itemResult {
             case .success:
-              // Advance over what was parsed.
-              consume(length: available - subDecoder.available)
-              consumed = true
+                // Advance over what was parsed.
+                consume(length: available - subDecoder.available)
+                consumed = true
             case .handleAsUnknown:
-              // Nothing to do.
-              break
+                // Nothing to do.
+                break
 
             case .malformed:
-              throw BinaryDecodingError.malformedProtobuf
+                throw BinaryDecodingError.malformedProtobuf
             }
 
             assert(recursionBudget == subDecoder.recursionBudget)
@@ -1134,21 +1150,21 @@ internal struct BinaryDecoder: Decoder {
     }
 
     private enum DecodeMessageSetItemResult {
-      case success
-      case handleAsUnknown
-      case malformed
+        case success
+        case handleAsUnknown
+        case malformed
     }
 
     private mutating func decodeMessageSetItem(
-      values: inout ExtensionFieldValueSet,
-      messageType: any Message.Type
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type
     ) throws -> DecodeMessageSetItemResult {
         // This is loosely based on the C++:
         //   ExtensionSet::ParseMessageSetItem()
         //   WireFormat::ParseAndMergeMessageSetItem()
         // (yes, there have two versions that are almost the same)
 
-      var msgExtension: (any AnyMessageExtension)?
+        var msgExtension: (any AnyMessageExtension)?
         var fieldData: Data?
 
         // In this loop, if wire types are wrong, things don't decode,
@@ -1170,19 +1186,23 @@ internal struct BinaryDecoder: Decoder {
                 if let data = fieldData {
                     var wasDecoded = false
                     try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-                      if let baseAddress = body.baseAddress, body.count > 0 {
-                        var extDecoder = BinaryDecoder(forReadingFrom: baseAddress,
-                                                       count: body.count,
-                                                       parent: self)
-                        // Prime the decode to be correct.
-                        extDecoder.consumed = false
-                        extDecoder.fieldWireFormat = .lengthDelimited
-                        try extDecoder.decodeExtensionField(values: &values,
-                                                            messageType: messageType,
-                                                            fieldNumber: ext.fieldNumber,
-                                                            messageExtension: ext)
-                        wasDecoded = extDecoder.consumed
-                      }
+                        if let baseAddress = body.baseAddress, body.count > 0 {
+                            var extDecoder = BinaryDecoder(
+                                forReadingFrom: baseAddress,
+                                count: body.count,
+                                parent: self
+                            )
+                            // Prime the decode to be correct.
+                            extDecoder.consumed = false
+                            extDecoder.fieldWireFormat = .lengthDelimited
+                            try extDecoder.decodeExtensionField(
+                                values: &values,
+                                messageType: messageType,
+                                fieldNumber: ext.fieldNumber,
+                                messageExtension: ext
+                            )
+                            wasDecoded = extDecoder.consumed
+                        }
                     }
                     if !wasDecoded {
                         return .malformed
@@ -1193,10 +1213,12 @@ internal struct BinaryDecoder: Decoder {
             case WireFormat.MessageSet.FieldNumbers.message:
                 if let ext = msgExtension {
                     assert(consumed == false)
-                    try decodeExtensionField(values: &values,
-                                             messageType: messageType,
-                                             fieldNumber: ext.fieldNumber,
-                                             messageExtension: ext)
+                    try decodeExtensionField(
+                        values: &values,
+                        messageType: messageType,
+                        fieldNumber: ext.fieldNumber,
+                        messageExtension: ext
+                    )
                     if !consumed {
                         return .malformed
                     }
@@ -1213,8 +1235,8 @@ internal struct BinaryDecoder: Decoder {
                         let payloadSize = Varint.encodedSize(of: Int64(data.count)) + data.count
                         var payload = Data(count: payloadSize)
                         payload.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
-                          var encoder = BinaryEncoder(forWritingInto: body)
-                          encoder.putBytesValue(value: data)
+                            var encoder = BinaryEncoder(forWritingInto: body)
+                            encoder.putBytesValue(value: data)
                         }
                         fieldData = payload
                     } else {
@@ -1394,7 +1416,7 @@ internal struct BinaryDecoder: Decoder {
     private mutating func decodeLittleEndianInteger<T: FixedWidthInteger>() throws -> T {
         let size = MemoryLayout<T>.size
         assert(size == 4 || size == 8)
-        guard available >= size else {throw BinaryDecodingError.truncated}
+        guard available >= size else { throw BinaryDecodingError.truncated }
         defer { consume(length: size) }
         return T(littleEndian: p.loadUnaligned(as: T.self))
     }
@@ -1423,13 +1445,13 @@ internal struct BinaryDecoder: Decoder {
         // This function does get called in some package decode handling, but
         // that is length delimited on the wire, so the spec would imply
         // the limit still applies.
-        guard length < 0x7fffffff else {
-          // Reuse existing error to avoid breaking change of changing thrown error
-          throw BinaryDecodingError.malformedProtobuf
+        guard length < 0x7fff_ffff else {
+            // Reuse existing error to avoid breaking change of changing thrown error
+            throw BinaryDecodingError.malformedProtobuf
         }
 
         guard length <= UInt64(available) else {
-          throw BinaryDecodingError.truncated
+            throw BinaryDecodingError.truncated
         }
 
         count = Int(length)

+ 21 - 21
Sources/SwiftProtobuf/BinaryDecodingError.swift

@@ -14,31 +14,31 @@
 
 /// Describes errors that can occur when decoding a message from binary format.
 public enum BinaryDecodingError: Error {
-  /// Extraneous data remained after decoding should have been complete.
-  case trailingGarbage
+    /// Extraneous data remained after decoding should have been complete.
+    case trailingGarbage
 
-  /// The decoder unexpectedly reached the end of the data before it was
-  /// expected.
-  case truncated
+    /// The decoder unexpectedly reached the end of the data before it was
+    /// expected.
+    case truncated
 
-  /// A string field was not encoded as valid UTF-8.
-  case invalidUTF8
+    /// A string field was not encoded as valid UTF-8.
+    case invalidUTF8
 
-  /// The binary data was malformed in some way, such as an invalid wire format
-  /// or field tag.
-  case malformedProtobuf
+    /// The binary data was malformed in some way, such as an invalid wire format
+    /// or field tag.
+    case malformedProtobuf
 
-  /// The definition of the message or one of its nested messages has required
-  /// fields but the binary data did not include values for them. You must pass
-  /// `partial: true` during decoding if you wish to explicitly ignore missing
-  /// required fields.
-  case missingRequiredFields
+    /// The definition of the message or one of its nested messages has required
+    /// fields but the binary data did not include values for them. You must pass
+    /// `partial: true` during decoding if you wish to explicitly ignore missing
+    /// required fields.
+    case missingRequiredFields
 
-  /// An internal error happened while decoding.  If this is ever encountered,
-  /// please file an issue with SwiftProtobuf with as much details as possible
-  /// for what happened (proto definitions, bytes being decoded (if possible)).
-  case internalExtensionError
+    /// An internal error happened while decoding.  If this is ever encountered,
+    /// please file an issue with SwiftProtobuf with as much details as possible
+    /// for what happened (proto definitions, bytes being decoded (if possible)).
+    case internalExtensionError
 
-  /// Reached the nesting limit for messages within messages while decoding.
-  case messageDepthLimit
+    /// Reached the nesting limit for messages within messages while decoding.
+    case messageDepthLimit
 }

+ 20 - 20
Sources/SwiftProtobuf/BinaryDecodingOptions.swift

@@ -14,26 +14,26 @@
 
 /// Options for binary decoding.
 public struct BinaryDecodingOptions: Sendable {
-  /// The maximum nesting of message with messages.  The default is 100.
-  ///
-  /// To prevent corrupt or malicious messages from causing stack overflows,
-  /// this controls how deep messages can be nested within other messages
-  /// while parsing.
-  public var messageDepthLimit: Int = 100
+    /// The maximum nesting of message with messages.  The default is 100.
+    ///
+    /// To prevent corrupt or malicious messages from causing stack overflows,
+    /// this controls how deep messages can be nested within other messages
+    /// while parsing.
+    public var messageDepthLimit: Int = 100
 
-  /// Discard unknown fields while parsing.  The default is false, so parsering
-  /// does not discard unknown fields.
-  ///
-  /// The Protobuf binary format allows unknown fields to be still parsed
-  /// so the schema can be expanded without requiring all readers to be updated.
-  /// This works in part by having any unknown fields preserved so they can
-  /// be relayed on without loss. For a while the proto3 syntax definition
-  /// called for unknown fields to be dropped, but that lead to problems in
-  /// some case. The default is to follow the spec and keep them, but setting
-  /// this option to `true` allows a developer to strip them during a parse
-  /// in case they have a specific need to drop the unknown fields from the
-  /// object graph being created.
-  public var discardUnknownFields: Bool = false
+    /// Discard unknown fields while parsing.  The default is false, so parsering
+    /// does not discard unknown fields.
+    ///
+    /// The Protobuf binary format allows unknown fields to be still parsed
+    /// so the schema can be expanded without requiring all readers to be updated.
+    /// This works in part by having any unknown fields preserved so they can
+    /// be relayed on without loss. For a while the proto3 syntax definition
+    /// called for unknown fields to be dropped, but that lead to problems in
+    /// some case. The default is to follow the spec and keep them, but setting
+    /// this option to `true` allows a developer to strip them during a parse
+    /// in case they have a specific need to drop the unknown fields from the
+    /// object graph being created.
+    public var discardUnknownFields: Bool = false
 
-  public init() {}
+    public init() {}
 }

+ 214 - 210
Sources/SwiftProtobuf/BinaryDelimited.swift

@@ -18,194 +18,198 @@ import Foundation
 
 /// Helper methods for reading/writing messages with a length prefix.
 public enum BinaryDelimited {
-  /// Additional errors for delimited message handing.
-  public enum Error: Swift.Error {
-    /// If a read/write to the stream fails, but the stream's `streamError` is nil,
-    /// this error will be throw instead since the stream didn't provide anything
-    /// more specific. A common cause for this can be failing to open the stream
-    /// before trying to read/write to it.
-    case unknownStreamError
+    /// Additional errors for delimited message handing.
+    public enum Error: Swift.Error {
+        /// If a read/write to the stream fails, but the stream's `streamError` is nil,
+        /// this error will be throw instead since the stream didn't provide anything
+        /// more specific. A common cause for this can be failing to open the stream
+        /// before trying to read/write to it.
+        case unknownStreamError
 
-    /// While reading/writing to the stream, less than the expected bytes was
-    /// read/written.
-    case truncated
-  }
-
-#if !os(WASI)
-  /// Serialize a single size-delimited message to the given stream. Delimited
-  /// format allows a single file or stream to contain multiple messages,
-  /// whereas normally writing multiple non-delimited messages to the same
-  /// stream would cause them to be merged. A delimited message is a varint
-  /// encoding the message size followed by a message of exactly that size.
-  ///
-  /// - Parameters:
-  ///   - message: The message to be written.
-  ///   - to: The `OutputStream` to write the message to.  The stream is
-  ///     is assumed to be ready to be written to.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryDelimited/Error/missingRequiredFields``.
-  /// - Throws: ``BinaryDelimited/Error`` if encoding fails or some writing errors occur; or the
-  /// underlying `OutputStream.streamError` for a stream error.
-  public static func serialize(
-    message: any Message,
-    to stream: OutputStream,
-    partial: Bool = false
-  ) throws {
-    // TODO: Revisit to avoid the extra buffering when encoding is streamed in general.
-    let serialized: [UInt8] = try message.serializedBytes(partial: partial)
-    let totalSize = Varint.encodedSize(of: UInt64(serialized.count)) + serialized.count
-    var bytes: [UInt8] = Array(repeating: 0, count: totalSize)
-    bytes.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
-      var encoder = BinaryEncoder(forWritingInto: body)
-      encoder.putBytesValue(value: serialized)
-    }
-
-    var written: Int = 0
-    bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-      if let baseAddress = body.baseAddress, body.count > 0 {
-        // This assumingMemoryBound is technically unsafe, but without SR-11078
-        // (https://bugs.swift.org/browse/SR-11087) we don't have another option.
-        // It should be "safe enough".
-        let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
-        written = stream.write(pointer, maxLength: totalSize)
-      }
+        /// While reading/writing to the stream, less than the expected bytes was
+        /// read/written.
+        case truncated
     }
 
-    if written != totalSize {
-      if written == -1 {
-        if let streamError = stream.streamError {
-          throw streamError
+    #if !os(WASI)
+    /// Serialize a single size-delimited message to the given stream. Delimited
+    /// format allows a single file or stream to contain multiple messages,
+    /// whereas normally writing multiple non-delimited messages to the same
+    /// stream would cause them to be merged. A delimited message is a varint
+    /// encoding the message size followed by a message of exactly that size.
+    ///
+    /// - Parameters:
+    ///   - message: The message to be written.
+    ///   - to: The `OutputStream` to write the message to.  The stream is
+    ///     is assumed to be ready to be written to.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryDelimited/Error/missingRequiredFields``.
+    /// - Throws: ``BinaryDelimited/Error`` if encoding fails or some writing errors occur; or the
+    /// underlying `OutputStream.streamError` for a stream error.
+    public static func serialize(
+        message: any Message,
+        to stream: OutputStream,
+        partial: Bool = false
+    ) throws {
+        // TODO: Revisit to avoid the extra buffering when encoding is streamed in general.
+        let serialized: [UInt8] = try message.serializedBytes(partial: partial)
+        let totalSize = Varint.encodedSize(of: UInt64(serialized.count)) + serialized.count
+        var bytes: [UInt8] = Array(repeating: 0, count: totalSize)
+        bytes.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
+            var encoder = BinaryEncoder(forWritingInto: body)
+            encoder.putBytesValue(value: serialized)
         }
-        throw BinaryDelimited.Error.unknownStreamError
-      }
-      throw BinaryDelimited.Error.truncated
-    }
-  }
 
-  /// Reads a single size-delimited message from the given stream. Delimited
-  /// format allows a single file or stream to contain multiple messages,
-  /// whereas normally parsing consumes the entire input. A delimited message
-  /// is a varint encoding the message size followed by a message of exactly
-  /// exactly that size.
-  ///
-  /// - Parameters:
-  ///   - messageType: The type of message to read.
-  ///   - from: The `InputStream` to read the data from.  The stream is assumed
-  ///     to be ready to read from.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryDecodingError/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Returns: The message read.
-  /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails,
-  /// some reading errors; or the underlying `InputStream.streamError` for a stream error.
-  public static func parse<M: Message>(
-    messageType: M.Type,
-    from stream: InputStream,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws -> M {
-    var message = M()
-    try merge(into: &message,
-              from: stream,
-              extensions: extensions,
-              partial: partial,
-              options: options)
-    return message
-  }
+        var written: Int = 0
+        bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+            if let baseAddress = body.baseAddress, body.count > 0 {
+                // This assumingMemoryBound is technically unsafe, but without SR-11078
+                // (https://bugs.swift.org/browse/SR-11087) we don't have another option.
+                // It should be "safe enough".
+                let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
+                written = stream.write(pointer, maxLength: totalSize)
+            }
+        }
 
-  /// Updates the message by reading a single size-delimited message from
-  /// the given stream. Delimited format allows a single file or stream to
-  /// contain multiple messages, whereas normally parsing consumes the entire
-  /// input. A delimited message is a varint encoding the message size
-  /// followed by a message of exactly that size.
-  ///
-  /// - Note: If this method throws an error, the message may still have been
-  ///   partially mutated by the binary data that was decoded before the error
-  ///   occurred.
-  ///
-  /// - Parameters:
-  ///   - mergingTo: The message to merge the data into.
-  ///   - from: The `InputStream` to read the data from.  The stream is assumed
-  ///     to be ready to read from.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryDelimited/Error/missingRequiredFields``.
-  ///   - options: The BinaryDecodingOptions to use.
-  /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails,
-  /// and for some reading errors; or the underlying `InputStream.streamError` for a stream error.
-  public static func merge<M: Message>(
-    into message: inout M,
-    from stream: InputStream,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    let unsignedLength = try decodeVarint(stream)
-    if unsignedLength == 0 {
-      // The message was all defaults, nothing to actually read.
-      return
-    }
-    guard unsignedLength <= 0x7fffffff else {
-      // Adding a new case is a breaking change, reuse malformedProtobuf.
-      throw BinaryDecodingError.malformedProtobuf
+        if written != totalSize {
+            if written == -1 {
+                if let streamError = stream.streamError {
+                    throw streamError
+                }
+                throw BinaryDelimited.Error.unknownStreamError
+            }
+            throw BinaryDelimited.Error.truncated
+        }
     }
-    let length = Int(unsignedLength)
 
-    // TODO: Consider doing a version with getBuffer:length: if the InputStream
-    // support it and thus avoiding this local copy.
+    /// Reads a single size-delimited message from the given stream. Delimited
+    /// format allows a single file or stream to contain multiple messages,
+    /// whereas normally parsing consumes the entire input. A delimited message
+    /// is a varint encoding the message size followed by a message of exactly
+    /// exactly that size.
+    ///
+    /// - Parameters:
+    ///   - messageType: The type of message to read.
+    ///   - from: The `InputStream` to read the data from.  The stream is assumed
+    ///     to be ready to read from.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryDecodingError/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Returns: The message read.
+    /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails,
+    /// some reading errors; or the underlying `InputStream.streamError` for a stream error.
+    public static func parse<M: Message>(
+        messageType: M.Type,
+        from stream: InputStream,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws -> M {
+        var message = M()
+        try merge(
+            into: &message,
+            from: stream,
+            extensions: extensions,
+            partial: partial,
+            options: options
+        )
+        return message
+    }
 
-    // Even though the bytes are read in chunks, things can still hard fail if
-    // there isn't enough memory to append to have all the bytes at once for
-    // parsing.
-    var data = [UInt8]()
-    let kChunkSize = 16 * 1024 * 1024
-    var chunk = [UInt8](repeating: 0, count: min(length, kChunkSize))
-    var bytesNeeded = length
-    while bytesNeeded > 0 {
-      let maxLength = min(bytesNeeded, chunk.count)
-      var bytesRead: Int = 0
-      chunk.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
-        if let baseAddress = body.baseAddress, body.count > 0 {
-          let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
-          bytesRead = stream.read(pointer, maxLength: maxLength)
+    /// Updates the message by reading a single size-delimited message from
+    /// the given stream. Delimited format allows a single file or stream to
+    /// contain multiple messages, whereas normally parsing consumes the entire
+    /// input. A delimited message is a varint encoding the message size
+    /// followed by a message of exactly that size.
+    ///
+    /// - Note: If this method throws an error, the message may still have been
+    ///   partially mutated by the binary data that was decoded before the error
+    ///   occurred.
+    ///
+    /// - Parameters:
+    ///   - mergingTo: The message to merge the data into.
+    ///   - from: The `InputStream` to read the data from.  The stream is assumed
+    ///     to be ready to read from.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryDelimited/Error/missingRequiredFields``.
+    ///   - options: The BinaryDecodingOptions to use.
+    /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails,
+    /// and for some reading errors; or the underlying `InputStream.streamError` for a stream error.
+    public static func merge<M: Message>(
+        into message: inout M,
+        from stream: InputStream,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        let unsignedLength = try decodeVarint(stream)
+        if unsignedLength == 0 {
+            // The message was all defaults, nothing to actually read.
+            return
         }
-      }
-      if bytesRead == -1 {
-        if let streamError = stream.streamError {
-          throw streamError
+        guard unsignedLength <= 0x7fff_ffff else {
+            // Adding a new case is a breaking change, reuse malformedProtobuf.
+            throw BinaryDecodingError.malformedProtobuf
         }
-        throw BinaryDelimited.Error.unknownStreamError
-      }
-      if bytesRead == 0 {
-        // Hit the end of the stream
-        throw BinaryDelimited.Error.truncated
-      }
-      if bytesRead < chunk.count {
-        data += chunk[0..<bytesRead]
-      } else {
-        data += chunk
-      }
-      bytesNeeded -= bytesRead
-    }
+        let length = Int(unsignedLength)
 
-    try message.merge(serializedBytes: data,
-                      extensions: extensions,
-                      partial: partial,
-                      options: options)
-  }
-#endif  // !os(WASI)
+        // TODO: Consider doing a version with getBuffer:length: if the InputStream
+        // support it and thus avoiding this local copy.
+
+        // Even though the bytes are read in chunks, things can still hard fail if
+        // there isn't enough memory to append to have all the bytes at once for
+        // parsing.
+        var data = [UInt8]()
+        let kChunkSize = 16 * 1024 * 1024
+        var chunk = [UInt8](repeating: 0, count: min(length, kChunkSize))
+        var bytesNeeded = length
+        while bytesNeeded > 0 {
+            let maxLength = min(bytesNeeded, chunk.count)
+            var bytesRead: Int = 0
+            chunk.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
+                if let baseAddress = body.baseAddress, body.count > 0 {
+                    let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
+                    bytesRead = stream.read(pointer, maxLength: maxLength)
+                }
+            }
+            if bytesRead == -1 {
+                if let streamError = stream.streamError {
+                    throw streamError
+                }
+                throw BinaryDelimited.Error.unknownStreamError
+            }
+            if bytesRead == 0 {
+                // Hit the end of the stream
+                throw BinaryDelimited.Error.truncated
+            }
+            if bytesRead < chunk.count {
+                data += chunk[0..<bytesRead]
+            } else {
+                data += chunk
+            }
+            bytesNeeded -= bytesRead
+        }
+
+        try message.merge(
+            serializedBytes: data,
+            extensions: extensions,
+            partial: partial,
+            options: options
+        )
+    }
+    #endif  // !os(WASI)
 }
 
 #if !os(WASI)
@@ -214,43 +218,43 @@ public enum BinaryDelimited {
 // a rewrite of BinaryDecoder.decodeVarint().
 internal func decodeVarint(_ stream: InputStream) throws -> UInt64 {
 
-  // Buffer to reuse within nextByte.
-  let readBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
-  defer { readBuffer.deallocate() }
+    // Buffer to reuse within nextByte.
+    let readBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
+    defer { readBuffer.deallocate() }
 
-  func nextByte() throws -> UInt8? {
-    let bytesRead = stream.read(readBuffer, maxLength: 1)
-    switch bytesRead {
-    case 1:
-      return readBuffer[0]
-    case 0:
-      return nil
-    default:
-      precondition(bytesRead == -1)
-      if let streamError = stream.streamError {
-        throw streamError
-      }
-      throw BinaryDelimited.Error.unknownStreamError
+    func nextByte() throws -> UInt8? {
+        let bytesRead = stream.read(readBuffer, maxLength: 1)
+        switch bytesRead {
+        case 1:
+            return readBuffer[0]
+        case 0:
+            return nil
+        default:
+            precondition(bytesRead == -1)
+            if let streamError = stream.streamError {
+                throw streamError
+            }
+            throw BinaryDelimited.Error.unknownStreamError
+        }
     }
-  }
 
-  var value: UInt64 = 0
-  var shift: UInt64 = 0
-  while true {
-    guard let c = try nextByte() else {
-      if shift == 0 {
-        throw SwiftProtobufError.BinaryStreamDecoding.noBytesAvailable()
-      }
-      throw BinaryDelimited.Error.truncated
-    }
-    value |= UInt64(c & 0x7f) << shift
-    if c & 0x80 == 0 {
-      return value
-    }
-    shift += 7
-    if shift > 63 {
-      throw BinaryDecodingError.malformedProtobuf
+    var value: UInt64 = 0
+    var shift: UInt64 = 0
+    while true {
+        guard let c = try nextByte() else {
+            if shift == 0 {
+                throw SwiftProtobufError.BinaryStreamDecoding.noBytesAvailable()
+            }
+            throw BinaryDelimited.Error.truncated
+        }
+        value |= UInt64(c & 0x7f) << shift
+        if c & 0x80 == 0 {
+            return value
+        }
+        shift += 7
+        if shift > 63 {
+            throw BinaryDecodingError.malformedProtobuf
+        }
     }
-  }
 }
 #endif  // !os(WASI)

+ 5 - 3
Sources/SwiftProtobuf/BinaryEncoder.swift

@@ -40,12 +40,14 @@ internal struct BinaryEncoder {
     }
 
     internal var used: Int {
-        return buffer.baseAddress!.distance(to: pointer)
+        buffer.baseAddress!.distance(to: pointer)
     }
 
     internal var remainder: UnsafeMutableRawBufferPointer {
-        return UnsafeMutableRawBufferPointer(start: pointer,
-	    count: buffer.count - used)
+        UnsafeMutableRawBufferPointer(
+            start: pointer,
+            count: buffer.count - used
+        )
     }
 
     internal mutating func advance(_ bytes: Int) {

+ 7 - 7
Sources/SwiftProtobuf/BinaryEncodingError.swift

@@ -14,11 +14,11 @@
 
 /// Describes errors that can occur when decoding a message from binary format.
 public enum BinaryEncodingError: Error, Hashable {
-  /// An unexpected failure when deserializing a `Google_Protobuf_Any`.
-  case anyTranscodeFailure
-  /// The definition of the message or one of its nested messages has required
-  /// fields but the message being encoded did not include values for them. You
-  /// must pass `partial: true` during encoding if you wish to explicitly ignore
-  /// missing required fields.
-  case missingRequiredFields
+    /// An unexpected failure when deserializing a `Google_Protobuf_Any`.
+    case anyTranscodeFailure
+    /// The definition of the message or one of its nested messages has required
+    /// fields but the message being encoded did not include values for them. You
+    /// must pass `partial: true` during encoding if you wish to explicitly ignore
+    /// missing required fields.
+    case missingRequiredFields
 }

+ 471 - 439
Sources/SwiftProtobuf/BinaryEncodingSizeVisitor.swift

@@ -20,454 +20,486 @@ import Foundation
 /// serialization.
 internal struct BinaryEncodingSizeVisitor: Visitor {
 
-  /// Accumulates the required size of the message during traversal.
-  var serializedSize: Int = 0
-
-  init() {}
-
-  mutating func visitUnknown(bytes: Data) throws {
-    serializedSize += bytes.count
-  }
-
-  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
-    serializedSize += tagSize + MemoryLayout<Float>.size
-  }
-
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
-    serializedSize += tagSize + MemoryLayout<Double>.size
-  }
-
-  mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
-    try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    serializedSize += tagSize + Varint.encodedSize(of: value)
-  }
-
-  mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
-    try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    serializedSize += tagSize + Varint.encodedSize(of: value)
-  }
-
-  mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value))
-  }
-
-  mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value))
-  }
-
-  mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
-    serializedSize += tagSize + MemoryLayout<UInt32>.size
-  }
-
-  mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
-    serializedSize += tagSize + MemoryLayout<UInt64>.size
-  }
-
-  mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
-    serializedSize += tagSize + MemoryLayout<Int32>.size
-  }
-
-  mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
-    serializedSize += tagSize + MemoryLayout<Int64>.size
-  }
-
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    serializedSize += tagSize + 1
-  }
-
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let count = value.utf8.count
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count
-  }
-
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let count = value.count
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count
-  }
-
-  // The default impls for visitRepeated*Field would work, but by implementing
-  // these directly, the calculation for the tag overhead can be optimized and
-  // the fixed width fields can be simple multiplication.
-
-  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
-    serializedSize += tagSize * value.count + MemoryLayout<Float>.size * value.count
-  }
-
-  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
-    serializedSize += tagSize * value.count + MemoryLayout<Double>.size * value.count
-  }
-
-  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
-    serializedSize += tagSize * value.count + MemoryLayout<UInt32>.size * value.count
-  }
-
-  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
-    serializedSize += tagSize * value.count + MemoryLayout<UInt64>.size * value.count
-  }
-
-  mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
-    serializedSize += tagSize * value.count + MemoryLayout<Int32>.size * value.count
-  }
-
-  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
-    serializedSize += tagSize * value.count + MemoryLayout<Int64>.size * value.count
-  }
-
-  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
-    serializedSize += tagSize * value.count + 1 * value.count
-  }
-
-  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) {
-      let count = $1.utf8.count
-      return $0 + Varint.encodedSize(of: Int64(count)) + count
-    }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) {
-      let count = $1.count
-      return $0 + Varint.encodedSize(of: Int64(count)) + count
-    }
-    serializedSize += tagSize * value.count + dataSize
-  }
-
-  // Packed field handling.
-
-  mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.count * MemoryLayout<Float>.size
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.count * MemoryLayout<Double>.size
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize +=
-      tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize +=
-      tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
-    serializedSize +=
-      tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
-    serializedSize +=
-      tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize +=
-      tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    serializedSize +=
-      tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.count * MemoryLayout<UInt32>.size
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.count * MemoryLayout<UInt64>.size
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.count * MemoryLayout<Int32>.size
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.count * MemoryLayout<Int64>.size
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
-    let dataSize = value.count
-    serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitSingularEnumField<E: Enum>(value: E,
-                                       fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .varint).encodedSize
-    serializedSize += tagSize
-    let dataSize = Varint.encodedSize(of: Int32(truncatingIfNeeded: value.rawValue))
-    serializedSize += dataSize
-  }
-
-  mutating func visitRepeatedEnumField<E: Enum>(value: [E],
-                                       fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .varint).encodedSize
-    serializedSize += value.count * tagSize
-    let dataSize = value.reduce(0) {
-      $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
-    }
-    serializedSize += dataSize
-  }
-
-  mutating func visitPackedEnumField<E: Enum>(value: [E],
-                                     fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .varint).encodedSize
-    serializedSize += tagSize
-    let dataSize = value.reduce(0) {
-      $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
-    }
-    serializedSize += Varint.encodedSize(of: Int64(dataSize)) + dataSize
-  }
-
-  mutating func visitSingularMessageField<M: Message>(value: M,
-                                             fieldNumber: Int) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .lengthDelimited).encodedSize
-    let messageSize = try value.serializedDataSize()
-    serializedSize +=
-      tagSize + Varint.encodedSize(of: UInt64(messageSize)) + messageSize
-  }
-
-  mutating func visitRepeatedMessageField<M: Message>(value: [M],
-                                             fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .lengthDelimited).encodedSize
-    serializedSize += value.count * tagSize
-    let dataSize = try value.reduce(0) {
-      let messageSize = try $1.serializedDataSize()
-      return $0 + Varint.encodedSize(of: UInt64(messageSize)) + messageSize
-    }
-    serializedSize += dataSize
-  }
-
-  mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
-    // The wire format doesn't matter here because the encoded size of the
-    // integer won't change based on the low three bits.
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .startGroup).encodedSize
-    serializedSize += 2 * tagSize
-    try value.traverse(visitor: &self)
-  }
-
-  mutating func visitRepeatedGroupField<G: Message>(value: [G],
-                                           fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .startGroup).encodedSize
-    serializedSize += 2 * value.count * tagSize
-    for v in value {
-      try v.traverse(visitor: &self)
-    }
-  }
-
-  mutating func visitMapField<KeyType, ValueType: MapValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: _ProtobufMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .lengthDelimited).encodedSize
-    for (k,v) in value {
-        var sizer = BinaryEncodingSizeVisitor()
-        try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
-        try ValueType.visitSingular(value: v, fieldNumber: 2, with: &sizer)
-        let entrySize = sizer.serializedSize
-        serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
-    }
-    serializedSize += value.count * tagSize
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where ValueType.RawValue == Int {
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .lengthDelimited).encodedSize
-    for (k,v) in value {
-        var sizer = BinaryEncodingSizeVisitor()
-        try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
-        try sizer.visitSingularEnumField(value: v, fieldNumber: 2)
-        let entrySize = sizer.serializedSize
-        serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
-    }
-    serializedSize += value.count * tagSize
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-    let tagSize = FieldTag(fieldNumber: fieldNumber,
-                           wireFormat: .lengthDelimited).encodedSize
-    for (k,v) in value {
-        var sizer = BinaryEncodingSizeVisitor()
-        try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
-        try sizer.visitSingularMessageField(value: v, fieldNumber: 2)
-        let entrySize = sizer.serializedSize
-        serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
-    }
-    serializedSize += value.count * tagSize
-  }
-
-  mutating func visitExtensionFieldsAsMessageSet(
-    fields: ExtensionFieldValueSet,
-    start: Int,
-    end: Int
-  ) throws {
-    var sizer = BinaryEncodingMessageSetSizeVisitor()
-    try fields.traverse(visitor: &sizer, start: start, end: end)
-    serializedSize += sizer.serializedSize
-  }
+    /// Accumulates the required size of the message during traversal.
+    var serializedSize: Int = 0
+
+    init() {}
+
+    mutating func visitUnknown(bytes: Data) throws {
+        serializedSize += bytes.count
+    }
+
+    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
+        serializedSize += tagSize + MemoryLayout<Float>.size
+    }
+
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
+        serializedSize += tagSize + MemoryLayout<Double>.size
+    }
+
+    mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        serializedSize += tagSize + Varint.encodedSize(of: value)
+    }
+
+    mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
+        try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        serializedSize += tagSize + Varint.encodedSize(of: value)
+    }
+
+    mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value))
+    }
+
+    mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value))
+    }
+
+    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
+        serializedSize += tagSize + MemoryLayout<UInt32>.size
+    }
+
+    mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
+        serializedSize += tagSize + MemoryLayout<UInt64>.size
+    }
+
+    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
+        serializedSize += tagSize + MemoryLayout<Int32>.size
+    }
+
+    mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
+        serializedSize += tagSize + MemoryLayout<Int64>.size
+    }
+
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        serializedSize += tagSize + 1
+    }
+
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let count = value.utf8.count
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count
+    }
+
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let count = value.count
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count
+    }
+
+    // The default impls for visitRepeated*Field would work, but by implementing
+    // these directly, the calculation for the tag overhead can be optimized and
+    // the fixed width fields can be simple multiplication.
+
+    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
+        serializedSize += tagSize * value.count + MemoryLayout<Float>.size * value.count
+    }
+
+    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
+        serializedSize += tagSize * value.count + MemoryLayout<Double>.size * value.count
+    }
+
+    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
+        serializedSize += tagSize * value.count + MemoryLayout<UInt32>.size * value.count
+    }
+
+    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
+        serializedSize += tagSize * value.count + MemoryLayout<UInt64>.size * value.count
+    }
+
+    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
+        serializedSize += tagSize * value.count + MemoryLayout<Int32>.size * value.count
+    }
+
+    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
+        serializedSize += tagSize * value.count + MemoryLayout<Int64>.size * value.count
+    }
+
+    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
+        serializedSize += tagSize * value.count + 1 * value.count
+    }
+
+    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) {
+            let count = $1.utf8.count
+            return $0 + Varint.encodedSize(of: Int64(count)) + count
+        }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) {
+            let count = $1.count
+            return $0 + Varint.encodedSize(of: Int64(count)) + count
+        }
+        serializedSize += tagSize * value.count + dataSize
+    }
+
+    // Packed field handling.
+
+    mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.count * MemoryLayout<Float>.size
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.count * MemoryLayout<Double>.size
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize +=
+            tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize +=
+            tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
+        serializedSize +=
+            tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
+        serializedSize +=
+            tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize +=
+            tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        serializedSize +=
+            tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.count * MemoryLayout<UInt32>.size
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.count * MemoryLayout<UInt64>.size
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.count * MemoryLayout<Int32>.size
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.count * MemoryLayout<Int64>.size
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
+        let dataSize = value.count
+        serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitSingularEnumField<E: Enum>(
+        value: E,
+        fieldNumber: Int
+    ) throws {
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .varint
+        ).encodedSize
+        serializedSize += tagSize
+        let dataSize = Varint.encodedSize(of: Int32(truncatingIfNeeded: value.rawValue))
+        serializedSize += dataSize
+    }
+
+    mutating func visitRepeatedEnumField<E: Enum>(
+        value: [E],
+        fieldNumber: Int
+    ) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .varint
+        ).encodedSize
+        serializedSize += value.count * tagSize
+        let dataSize = value.reduce(0) {
+            $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
+        }
+        serializedSize += dataSize
+    }
+
+    mutating func visitPackedEnumField<E: Enum>(
+        value: [E],
+        fieldNumber: Int
+    ) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .varint
+        ).encodedSize
+        serializedSize += tagSize
+        let dataSize = value.reduce(0) {
+            $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
+        }
+        serializedSize += Varint.encodedSize(of: Int64(dataSize)) + dataSize
+    }
+
+    mutating func visitSingularMessageField<M: Message>(
+        value: M,
+        fieldNumber: Int
+    ) throws {
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .lengthDelimited
+        ).encodedSize
+        let messageSize = try value.serializedDataSize()
+        serializedSize +=
+            tagSize + Varint.encodedSize(of: UInt64(messageSize)) + messageSize
+    }
+
+    mutating func visitRepeatedMessageField<M: Message>(
+        value: [M],
+        fieldNumber: Int
+    ) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .lengthDelimited
+        ).encodedSize
+        serializedSize += value.count * tagSize
+        let dataSize = try value.reduce(0) {
+            let messageSize = try $1.serializedDataSize()
+            return $0 + Varint.encodedSize(of: UInt64(messageSize)) + messageSize
+        }
+        serializedSize += dataSize
+    }
+
+    mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
+        // The wire format doesn't matter here because the encoded size of the
+        // integer won't change based on the low three bits.
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .startGroup
+        ).encodedSize
+        serializedSize += 2 * tagSize
+        try value.traverse(visitor: &self)
+    }
+
+    mutating func visitRepeatedGroupField<G: Message>(
+        value: [G],
+        fieldNumber: Int
+    ) throws {
+        assert(!value.isEmpty)
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .startGroup
+        ).encodedSize
+        serializedSize += 2 * value.count * tagSize
+        for v in value {
+            try v.traverse(visitor: &self)
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .lengthDelimited
+        ).encodedSize
+        for (k, v) in value {
+            var sizer = BinaryEncodingSizeVisitor()
+            try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
+            try ValueType.visitSingular(value: v, fieldNumber: 2, with: &sizer)
+            let entrySize = sizer.serializedSize
+            serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
+        }
+        serializedSize += value.count * tagSize
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where ValueType.RawValue == Int {
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .lengthDelimited
+        ).encodedSize
+        for (k, v) in value {
+            var sizer = BinaryEncodingSizeVisitor()
+            try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
+            try sizer.visitSingularEnumField(value: v, fieldNumber: 2)
+            let entrySize = sizer.serializedSize
+            serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
+        }
+        serializedSize += value.count * tagSize
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        let tagSize = FieldTag(
+            fieldNumber: fieldNumber,
+            wireFormat: .lengthDelimited
+        ).encodedSize
+        for (k, v) in value {
+            var sizer = BinaryEncodingSizeVisitor()
+            try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
+            try sizer.visitSingularMessageField(value: v, fieldNumber: 2)
+            let entrySize = sizer.serializedSize
+            serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
+        }
+        serializedSize += value.count * tagSize
+    }
+
+    mutating func visitExtensionFieldsAsMessageSet(
+        fields: ExtensionFieldValueSet,
+        start: Int,
+        end: Int
+    ) throws {
+        var sizer = BinaryEncodingMessageSetSizeVisitor()
+        try fields.traverse(visitor: &sizer, start: start, end: end)
+        serializedSize += sizer.serializedSize
+    }
 }
 
 extension BinaryEncodingSizeVisitor {
 
-  // Helper Visitor to compute the sizes when writing out the extensions as MessageSets.
-  internal struct BinaryEncodingMessageSetSizeVisitor: SelectiveVisitor {
-    var serializedSize: Int = 0
+    // Helper Visitor to compute the sizes when writing out the extensions as MessageSets.
+    internal struct BinaryEncodingMessageSetSizeVisitor: SelectiveVisitor {
+        var serializedSize: Int = 0
 
-    init() {}
+        init() {}
 
-    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
-      var groupSize = WireFormat.MessageSet.itemTagsEncodedSize
+        mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
+            var groupSize = WireFormat.MessageSet.itemTagsEncodedSize
 
-      groupSize += Varint.encodedSize(of: Int32(fieldNumber))
+            groupSize += Varint.encodedSize(of: Int32(fieldNumber))
 
-      let messageSize = try value.serializedDataSize()
-      groupSize += Varint.encodedSize(of: UInt64(messageSize)) + messageSize
+            let messageSize = try value.serializedDataSize()
+            groupSize += Varint.encodedSize(of: UInt64(messageSize)) + messageSize
 
-      serializedSize += groupSize
-    }
+            serializedSize += groupSize
+        }
 
-    // SelectiveVisitor handles the rest.
-  }
+        // SelectiveVisitor handles the rest.
+    }
 
 }

+ 368 - 353
Sources/SwiftProtobuf/BinaryEncodingVisitor.swift

@@ -17,371 +17,386 @@ import Foundation
 
 /// Visitor that encodes a message graph in the protobuf binary wire format.
 internal struct BinaryEncodingVisitor: Visitor {
-  private let options: BinaryEncodingOptions
-
-  var encoder: BinaryEncoder
-
-  /// Creates a new visitor that writes the binary-coded message into the memory
-  /// at the given pointer.
-  ///
-  /// - Precondition: `pointer` must point to an allocated block of memory that
-  ///   is large enough to hold the entire encoded message. For performance
-  ///   reasons, the encoder does not make any attempts to verify this.
-  init(forWritingInto buffer: UnsafeMutableRawBufferPointer, options: BinaryEncodingOptions) {
-    self.encoder = BinaryEncoder(forWritingInto: buffer)
-    self.options = options
-  }
-
-  mutating func visitUnknown(bytes: Data) throws {
-    encoder.appendUnknown(data: bytes)
-  }
-
-  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32)
-    encoder.putFloatValue(value: value)
-  }
-
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64)
-    encoder.putDoubleValue(value: value)
-  }
-
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-    try visitSingularUInt64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .varint)
-    encoder.putVarInt(value: value)
-  }
-
-  mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
-    try visitSingularSInt64Field(value: Int64(value), fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
-    try visitSingularUInt64Field(value: ZigZag.encoded(value), fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32)
-    encoder.putFixedUInt32(value: value)
-  }
-
-  mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64)
-    encoder.putFixedUInt64(value: value)
-  }
-
-  mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
-    try visitSingularFixed32Field(value: UInt32(bitPattern: value), fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
-    try visitSingularFixed64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-    try visitSingularUInt64Field(value: value ? 1 : 0, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putStringValue(value: value)
-  }
-
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putBytesValue(value: value)
-  }
-
-  mutating func visitSingularEnumField<E: Enum>(value: E,
-                                                fieldNumber: Int) throws {
-    try visitSingularUInt64Field(value: UInt64(bitPattern: Int64(value.rawValue)),
-                                 fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularMessageField<M: Message>(value: M,
-                                             fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let length = try value.serializedDataSize()
-    encoder.putVarInt(value: length)
-    try value.traverse(visitor: &self)
-  }
-
-  mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .startGroup)
-    try value.traverse(visitor: &self)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .endGroup)
-  }
-
-  // Repeated fields are handled by the default implementations in Visitor.swift
-
-
-  // Packed Fields
-
-  mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putVarInt(value: value.count * MemoryLayout<Float>.size)
-    for v in value {
-      encoder.putFloatValue(value: v)
-    }
-  }
-
-  mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putVarInt(value: value.count * MemoryLayout<Double>.size)
-    for v in value {
-      encoder.putDoubleValue(value: v)
-    }
-  }
-
-  mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    encoder.putVarInt(value: packedSize)
-    for v in value {
-        encoder.putVarInt(value: Int64(v))
-    }
-  }
-
-  mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    encoder.putVarInt(value: packedSize)
-    for v in value {
-        encoder.putVarInt(value: v)
-    }
-  }
-
-  mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
-    encoder.putVarInt(value: packedSize)
-    for v in value {
-        encoder.putZigZagVarInt(value: Int64(v))
-    }
-  }
-
-  mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
-    encoder.putVarInt(value: packedSize)
-    for v in value {
-        encoder.putZigZagVarInt(value: v)
-    }
-  }
-
-  mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    encoder.putVarInt(value: packedSize)
-    for v in value {
-        encoder.putVarInt(value: UInt64(v))
-    }
-  }
-
-  mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
-    encoder.putVarInt(value: packedSize)
-    for v in value {
-        encoder.putVarInt(value: v)
-    }
-  }
-
-  mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putVarInt(value: value.count * MemoryLayout<UInt32>.size)
-    for v in value {
-      encoder.putFixedUInt32(value: v)
-    }
-  }
-
-  mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putVarInt(value: value.count * MemoryLayout<UInt64>.size)
-    for v in value {
-      encoder.putFixedUInt64(value: v)
-    }
-  }
-
-  mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putVarInt(value: value.count * MemoryLayout<Int32>.size)
-    for v in value {
-       encoder.putFixedUInt32(value: UInt32(bitPattern: v))
-    }
-  }
-
-  mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putVarInt(value: value.count * MemoryLayout<Int64>.size)
-    for v in value {
-      encoder.putFixedUInt64(value: UInt64(bitPattern: v))
-    }
-  }
-
-  mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    encoder.putVarInt(value: value.count)
-    for v in value {
-      encoder.putVarInt(value: v ? 1 : 0)
-    }
-  }
-
-  mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-    let packedSize = value.reduce(0) {
-      $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
-    }
-    encoder.putVarInt(value: packedSize)
-    for v in value {
-      encoder.putVarInt(value: v.rawValue)
-    }
-  }
-
-  mutating func visitMapField<KeyType, ValueType: MapValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: _ProtobufMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-    try iterateAndEncode(
-      map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan,
-      encodeWithSizer: { sizer, key, value in
-        try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
-        try ValueType.visitSingular(value: value, fieldNumber: 2, with: &sizer)
-      }, encodeWithVisitor: { visitor, key, value in
-        try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-        try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
-      }
-    )
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where ValueType.RawValue == Int {
-    try iterateAndEncode(
-      map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan,
-      encodeWithSizer: { sizer, key, value in
-        try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
-        try sizer.visitSingularEnumField(value: value, fieldNumber: 2)
-      }, encodeWithVisitor: { visitor, key, value in
-        try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-        try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
-      }
-    )
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-    try iterateAndEncode(
-      map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan,
-      encodeWithSizer: { sizer, key, value in
-        try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
-        try sizer.visitSingularMessageField(value: value, fieldNumber: 2)
-      }, encodeWithVisitor: { visitor, key, value in
-        try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-        try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
-      }
-    )
-  }
-
-  /// Helper to encapsulate the common structure of iterating over a map
-  /// and encoding the keys and values.
-  private mutating func iterateAndEncode<K, V>(
-    map: Dictionary<K, V>,
-    fieldNumber: Int,
-    isOrderedBefore: (K, K) -> Bool,
-    encodeWithSizer: (inout BinaryEncodingSizeVisitor, K, V) throws -> (),
-    encodeWithVisitor: (inout BinaryEncodingVisitor, K, V) throws -> ()
-  ) throws {
-    if options.useDeterministicOrdering {
-      for (k,v) in map.sorted(by: { isOrderedBefore( $0.0, $1.0) }) {
+    private let options: BinaryEncodingOptions
+
+    var encoder: BinaryEncoder
+
+    /// Creates a new visitor that writes the binary-coded message into the memory
+    /// at the given pointer.
+    ///
+    /// - Precondition: `pointer` must point to an allocated block of memory that
+    ///   is large enough to hold the entire encoded message. For performance
+    ///   reasons, the encoder does not make any attempts to verify this.
+    init(forWritingInto buffer: UnsafeMutableRawBufferPointer, options: BinaryEncodingOptions) {
+        self.encoder = BinaryEncoder(forWritingInto: buffer)
+        self.options = options
+    }
+
+    mutating func visitUnknown(bytes: Data) throws {
+        encoder.appendUnknown(data: bytes)
+    }
+
+    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32)
+        encoder.putFloatValue(value: value)
+    }
+
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64)
+        encoder.putDoubleValue(value: value)
+    }
+
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        try visitSingularUInt64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .varint)
+        encoder.putVarInt(value: value)
+    }
+
+    mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularSInt64Field(value: Int64(value), fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
+        try visitSingularUInt64Field(value: ZigZag.encoded(value), fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32)
+        encoder.putFixedUInt32(value: value)
+    }
+
+    mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64)
+        encoder.putFixedUInt64(value: value)
+    }
+
+    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularFixed32Field(value: UInt32(bitPattern: value), fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
+        try visitSingularFixed64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        try visitSingularUInt64Field(value: value ? 1 : 0, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
         encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-        var sizer = BinaryEncodingSizeVisitor()
-        try encodeWithSizer(&sizer, k, v)
-        let entrySize = sizer.serializedSize
-        encoder.putVarInt(value: entrySize)
-        try encodeWithVisitor(&self, k, v)
-      }
-    } else {
-      for (k,v) in map {
+        encoder.putStringValue(value: value)
+    }
+
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
         encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
-        var sizer = BinaryEncodingSizeVisitor()
-        try encodeWithSizer(&sizer, k, v)
-        let entrySize = sizer.serializedSize
-        encoder.putVarInt(value: entrySize)
-        try encodeWithVisitor(&self, k, v)
-      }
-    }
-  }
-
-  mutating func visitExtensionFieldsAsMessageSet(
-    fields: ExtensionFieldValueSet,
-    start: Int,
-    end: Int
-  ) throws {
-    var subVisitor = BinaryEncodingMessageSetVisitor(encoder: encoder, options: options)
-    try fields.traverse(visitor: &subVisitor, start: start, end: end)
-    encoder = subVisitor.encoder
-  }
-}
+        encoder.putBytesValue(value: value)
+    }
 
-extension BinaryEncodingVisitor {
+    mutating func visitSingularEnumField<E: Enum>(
+        value: E,
+        fieldNumber: Int
+    ) throws {
+        try visitSingularUInt64Field(
+            value: UInt64(bitPattern: Int64(value.rawValue)),
+            fieldNumber: fieldNumber
+        )
+    }
 
-  // Helper Visitor to when writing out the extensions as MessageSets.
-  internal struct BinaryEncodingMessageSetVisitor: SelectiveVisitor {
-    private let options: BinaryEncodingOptions
+    mutating func visitSingularMessageField<M: Message>(
+        value: M,
+        fieldNumber: Int
+    ) throws {
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let length = try value.serializedDataSize()
+        encoder.putVarInt(value: length)
+        try value.traverse(visitor: &self)
+    }
 
-    var encoder: BinaryEncoder
+    mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .startGroup)
+        try value.traverse(visitor: &self)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .endGroup)
+    }
 
-    init(encoder: BinaryEncoder, options: BinaryEncodingOptions) {
-      self.options = options
-      self.encoder = encoder
+    // Repeated fields are handled by the default implementations in Visitor.swift
+
+    // Packed Fields
+
+    mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        encoder.putVarInt(value: value.count * MemoryLayout<Float>.size)
+        for v in value {
+            encoder.putFloatValue(value: v)
+        }
     }
 
-    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
-      encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemStart.rawValue))
+    mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        encoder.putVarInt(value: value.count * MemoryLayout<Double>.size)
+        for v in value {
+            encoder.putDoubleValue(value: v)
+        }
+    }
 
-      encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.typeId.rawValue))
-      encoder.putVarInt(value: fieldNumber)
+    mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        encoder.putVarInt(value: packedSize)
+        for v in value {
+            encoder.putVarInt(value: Int64(v))
+        }
+    }
 
-      encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.message.rawValue))
+    mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        encoder.putVarInt(value: packedSize)
+        for v in value {
+            encoder.putVarInt(value: v)
+        }
+    }
 
-      // Use a normal BinaryEncodingVisitor so any message fields end up in the
-      // normal wire format (instead of MessageSet format).
-      let length = try value.serializedDataSize()
-      encoder.putVarInt(value: length)
-      // Create the sub encoder after writing the length.
-      var subVisitor = BinaryEncodingVisitor(
-        forWritingInto: encoder.remainder, options: options
-      )
-      try value.traverse(visitor: &subVisitor)
-      encoder.advance(subVisitor.encoder.used)
+    mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
+        encoder.putVarInt(value: packedSize)
+        for v in value {
+            encoder.putZigZagVarInt(value: Int64(v))
+        }
+    }
 
-      encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemEnd.rawValue))
+    mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
+        encoder.putVarInt(value: packedSize)
+        for v in value {
+            encoder.putZigZagVarInt(value: v)
+        }
     }
 
-    // SelectiveVisitor handles the rest.
-  }
+    mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        encoder.putVarInt(value: packedSize)
+        for v in value {
+            encoder.putVarInt(value: UInt64(v))
+        }
+    }
+
+    mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
+        encoder.putVarInt(value: packedSize)
+        for v in value {
+            encoder.putVarInt(value: v)
+        }
+    }
+
+    mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        encoder.putVarInt(value: value.count * MemoryLayout<UInt32>.size)
+        for v in value {
+            encoder.putFixedUInt32(value: v)
+        }
+    }
+
+    mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        encoder.putVarInt(value: value.count * MemoryLayout<UInt64>.size)
+        for v in value {
+            encoder.putFixedUInt64(value: v)
+        }
+    }
+
+    mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        encoder.putVarInt(value: value.count * MemoryLayout<Int32>.size)
+        for v in value {
+            encoder.putFixedUInt32(value: UInt32(bitPattern: v))
+        }
+    }
+
+    mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        encoder.putVarInt(value: value.count * MemoryLayout<Int64>.size)
+        for v in value {
+            encoder.putFixedUInt64(value: UInt64(bitPattern: v))
+        }
+    }
+
+    mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        encoder.putVarInt(value: value.count)
+        for v in value {
+            encoder.putVarInt(value: v ? 1 : 0)
+        }
+    }
+
+    mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let packedSize = value.reduce(0) {
+            $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
+        }
+        encoder.putVarInt(value: packedSize)
+        for v in value {
+            encoder.putVarInt(value: v.rawValue)
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        try iterateAndEncode(
+            map: value,
+            fieldNumber: fieldNumber,
+            isOrderedBefore: KeyType._lessThan,
+            encodeWithSizer: { sizer, key, value in
+                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
+                try ValueType.visitSingular(value: value, fieldNumber: 2, with: &sizer)
+            },
+            encodeWithVisitor: { visitor, key, value in
+                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+                try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
+            }
+        )
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where ValueType.RawValue == Int {
+        try iterateAndEncode(
+            map: value,
+            fieldNumber: fieldNumber,
+            isOrderedBefore: KeyType._lessThan,
+            encodeWithSizer: { sizer, key, value in
+                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
+                try sizer.visitSingularEnumField(value: value, fieldNumber: 2)
+            },
+            encodeWithVisitor: { visitor, key, value in
+                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+                try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
+            }
+        )
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        try iterateAndEncode(
+            map: value,
+            fieldNumber: fieldNumber,
+            isOrderedBefore: KeyType._lessThan,
+            encodeWithSizer: { sizer, key, value in
+                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer)
+                try sizer.visitSingularMessageField(value: value, fieldNumber: 2)
+            },
+            encodeWithVisitor: { visitor, key, value in
+                try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+                try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
+            }
+        )
+    }
+
+    /// Helper to encapsulate the common structure of iterating over a map
+    /// and encoding the keys and values.
+    private mutating func iterateAndEncode<K, V>(
+        map: [K: V],
+        fieldNumber: Int,
+        isOrderedBefore: (K, K) -> Bool,
+        encodeWithSizer: (inout BinaryEncodingSizeVisitor, K, V) throws -> Void,
+        encodeWithVisitor: (inout BinaryEncodingVisitor, K, V) throws -> Void
+    ) throws {
+        if options.useDeterministicOrdering {
+            for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) {
+                encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+                var sizer = BinaryEncodingSizeVisitor()
+                try encodeWithSizer(&sizer, k, v)
+                let entrySize = sizer.serializedSize
+                encoder.putVarInt(value: entrySize)
+                try encodeWithVisitor(&self, k, v)
+            }
+        } else {
+            for (k, v) in map {
+                encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+                var sizer = BinaryEncodingSizeVisitor()
+                try encodeWithSizer(&sizer, k, v)
+                let entrySize = sizer.serializedSize
+                encoder.putVarInt(value: entrySize)
+                try encodeWithVisitor(&self, k, v)
+            }
+        }
+    }
+
+    mutating func visitExtensionFieldsAsMessageSet(
+        fields: ExtensionFieldValueSet,
+        start: Int,
+        end: Int
+    ) throws {
+        var subVisitor = BinaryEncodingMessageSetVisitor(encoder: encoder, options: options)
+        try fields.traverse(visitor: &subVisitor, start: start, end: end)
+        encoder = subVisitor.encoder
+    }
+}
+
+extension BinaryEncodingVisitor {
+
+    // Helper Visitor to when writing out the extensions as MessageSets.
+    internal struct BinaryEncodingMessageSetVisitor: SelectiveVisitor {
+        private let options: BinaryEncodingOptions
+
+        var encoder: BinaryEncoder
+
+        init(encoder: BinaryEncoder, options: BinaryEncodingOptions) {
+            self.options = options
+            self.encoder = encoder
+        }
+
+        mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
+            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemStart.rawValue))
+
+            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.typeId.rawValue))
+            encoder.putVarInt(value: fieldNumber)
+
+            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.message.rawValue))
+
+            // Use a normal BinaryEncodingVisitor so any message fields end up in the
+            // normal wire format (instead of MessageSet format).
+            let length = try value.serializedDataSize()
+            encoder.putVarInt(value: length)
+            // Create the sub encoder after writing the length.
+            var subVisitor = BinaryEncodingVisitor(
+                forWritingInto: encoder.remainder,
+                options: options
+            )
+            try value.traverse(visitor: &subVisitor)
+            encoder.advance(subVisitor.encoder.used)
+
+            encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemEnd.rawValue))
+        }
+
+        // SelectiveVisitor handles the rest.
+    }
 }

+ 15 - 15
Sources/SwiftProtobuf/CustomJSONCodable.swift

@@ -14,23 +14,23 @@
 
 /// Allows WKTs to provide their custom JSON encodings.
 internal protocol _CustomJSONCodable {
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String
-  mutating func decodeJSON(from: inout JSONDecoder) throws
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String
+    mutating func decodeJSON(from: inout JSONDecoder) throws
 
-  /// Called when the JSON `null` literal is encountered in a position where
-  /// a message of the conforming type is expected. The message type can then
-  /// handle the `null` value differently, if needed; for example,
-  /// `Google_Protobuf_Value` returns a special instance whose `kind` is set to
-  /// `.nullValue(.nullValue)`.
-  ///
-  /// The default behavior is to return `nil`, which indicates that `null`
-  /// should be treated as the absence of a message.
-  static func decodedFromJSONNull() throws -> Self?
+    /// Called when the JSON `null` literal is encountered in a position where
+    /// a message of the conforming type is expected. The message type can then
+    /// handle the `null` value differently, if needed; for example,
+    /// `Google_Protobuf_Value` returns a special instance whose `kind` is set to
+    /// `.nullValue(.nullValue)`.
+    ///
+    /// The default behavior is to return `nil`, which indicates that `null`
+    /// should be treated as the absence of a message.
+    static func decodedFromJSONNull() throws -> Self?
 }
 
 extension _CustomJSONCodable {
-  internal static func decodedFromJSONNull() -> Self? {
-    // Return nil by default. Concrete types can provide custom logic.
-    return nil
-  }
+    internal static func decodedFromJSONNull() -> Self? {
+        // Return nil by default. Concrete types can provide custom logic.
+        nil
+    }
 }

+ 180 - 163
Sources/SwiftProtobuf/Decoder.swift

@@ -55,174 +55,191 @@ import Foundation
 /// polluting the generated `Enum` and `Message` types with all of the
 /// necessary generic methods to support this.
 public protocol Decoder {
-  /// Called by a `oneof` when it already has a value and is being asked to
-  /// accept a new value. Some formats require `oneof` decoding to fail in this
-  /// case.
-  mutating func handleConflictingOneOf() throws
-
-  /// Returns the next field number, or nil when the end of the input is
-  /// reached.
-  ///
-  /// For JSON and text format, the decoder translates the field name to a
-  /// number at this point, based on information it obtained from the message
-  /// when it was initialized.
-  mutating func nextFieldNumber() throws -> Int?
-
-  /// Decode a float value to non-`Optional` field storage
-  mutating func decodeSingularFloatField(value: inout Float) throws
-  /// Decode a float value to `Optional` field storage
-  mutating func decodeSingularFloatField(value: inout Float?) throws
-  /// Decode float values to repeated field storage
-  mutating func decodeRepeatedFloatField(value: inout [Float]) throws
-  /// Decode a double value to non-`Optional` field storage
-  mutating func decodeSingularDoubleField(value: inout Double) throws
-  /// Decode a double value to `Optional` field storage
-  mutating func decodeSingularDoubleField(value: inout Double?) throws
-  /// Decode double values to repeated field storage
-  mutating func decodeRepeatedDoubleField(value: inout [Double]) throws
-  /// Decode an int32 value to non-`Optional` field storage
-  mutating func decodeSingularInt32Field(value: inout Int32) throws
-  /// Decode an int32 value to `Optional` field storage
-  mutating func decodeSingularInt32Field(value: inout Int32?) throws
-  /// Decode int32 values to repeated field storage
-  mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws
-  /// Decode an int64 value to non-`Optional` field storage
-  mutating func decodeSingularInt64Field(value: inout Int64) throws
-  /// Decode an int64 value to `Optional` field storage
-  mutating func decodeSingularInt64Field(value: inout Int64?) throws
-  /// Decode int64 values to repeated field storage
-  mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws
-  /// Decode a uint32 value to non-`Optional` field storage
-  mutating func decodeSingularUInt32Field(value: inout UInt32) throws
-  /// Decode a uint32 value to `Optional` field storage
-  mutating func decodeSingularUInt32Field(value: inout UInt32?) throws
-  /// Decode uint32 values to repeated field storage
-  mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws
-  /// Decode a uint64 value to non-`Optional` field storage
-  mutating func decodeSingularUInt64Field(value: inout UInt64) throws
-  /// Decode a uint64 value to `Optional` field storage
-  mutating func decodeSingularUInt64Field(value: inout UInt64?) throws
-  /// Decode uint64 values to repeated field storage
-  mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws
-  /// Decode an sint32 value to non-`Optional` field storage
-  mutating func decodeSingularSInt32Field(value: inout Int32) throws
-  /// Decode an sint32 value to `Optional` field storage
-  mutating func decodeSingularSInt32Field(value: inout Int32?) throws
-  /// Decode sint32 values to repeated field storage
-  mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws
-  /// Decode an sint64 value to non-`Optional` field storage
-  mutating func decodeSingularSInt64Field(value: inout Int64) throws
-  /// Decode an sint64 value to `Optional` field storage
-  mutating func decodeSingularSInt64Field(value: inout Int64?) throws
-  /// Decode sint64 values to repeated field storage
-  mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws
-  /// Decode a fixed32 value to non-`Optional` field storage
-  mutating func decodeSingularFixed32Field(value: inout UInt32) throws
-  /// Decode a fixed32 value to `Optional` field storage
-  mutating func decodeSingularFixed32Field(value: inout UInt32?) throws
-  /// Decode fixed32 values to repeated field storage
-  mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws
-  /// Decode a fixed64 value to non-`Optional` field storage
-  mutating func decodeSingularFixed64Field(value: inout UInt64) throws
-  /// Decode a fixed64 value to `Optional` field storage
-  mutating func decodeSingularFixed64Field(value: inout UInt64?) throws
-  /// Decode fixed64 values to repeated field storage
-  mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws
-  /// Decode an sfixed32 value to non-`Optional` field storage
-  mutating func decodeSingularSFixed32Field(value: inout Int32) throws
-  /// Decode an sfixed32 value to `Optional` field storage
-  mutating func decodeSingularSFixed32Field(value: inout Int32?) throws
-  /// Decode sfixed32 values to repeated field storage
-  mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws
-  /// Decode an sfixed64 value to non-`Optional` field storage
-  mutating func decodeSingularSFixed64Field(value: inout Int64) throws
-  /// Decode an sfixed64 value to `Optional` field storage
-  mutating func decodeSingularSFixed64Field(value: inout Int64?) throws
-  /// Decode sfixed64 values to repeated field storage
-  mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws
-  /// Decode a bool value to non-`Optional` field storage
-  mutating func decodeSingularBoolField(value: inout Bool) throws
-  /// Decode a bool value to `Optional` field storage
-  mutating func decodeSingularBoolField(value: inout Bool?) throws
-  /// Decode bool values to repeated field storage
-  mutating func decodeRepeatedBoolField(value: inout [Bool]) throws
-  /// Decode a string value to non-`Optional` field storage
-  mutating func decodeSingularStringField(value: inout String) throws
-  /// Decode a string value to `Optional` field storage
-  mutating func decodeSingularStringField(value: inout String?) throws
-  /// Decode string values to repeated field storage
-  mutating func decodeRepeatedStringField(value: inout [String]) throws
-  /// Decode a bytes value to non-`Optional` field storage
-  mutating func decodeSingularBytesField(value: inout Data) throws
-  /// Decode a bytes value to `Optional` field storage
-  mutating func decodeSingularBytesField(value: inout Data?) throws
-  /// Decode bytes values to repeated field storage
-  mutating func decodeRepeatedBytesField(value: inout [Data]) throws
-
-  // Decode Enum fields
-
-  /// Decode an enum value to non-`Optional` field storage
-  mutating func decodeSingularEnumField<E: Enum>(value: inout E) throws where E.RawValue == Int
-  /// Decode an enum value to `Optional` field storage
-  mutating func decodeSingularEnumField<E: Enum>(value: inout E?) throws where E.RawValue == Int
-  /// Decode enum values to repeated field storage
-  mutating func decodeRepeatedEnumField<E: Enum>(value: inout [E]) throws where E.RawValue == Int
-
-  // Decode Message fields
-
-  /// Decode a message value to `Optional` field storage.
-  ///
-  /// Unlike the primitive types, message fields are always stored
-  /// as Swift `Optional` values.
-  mutating func decodeSingularMessageField<M: Message>(value: inout M?) throws
-  /// Decode message values to repeated field storage
-  mutating func decodeRepeatedMessageField<M: Message>(value: inout [M]) throws
-
-  // Decode Group fields
-
-  /// Decode a group value to `Optional` field storage.
-  ///
-  /// Unlike the primitive types, message fields are always stored
-  /// as Swift `Optional` values.
-  /// Note that groups are only used in proto2.
-  mutating func decodeSingularGroupField<G: Message>(value: inout G?) throws
-  /// Decode group values to repeated field storage
-  mutating func decodeRepeatedGroupField<G: Message>(value: inout [G]) throws
-
-  // Decode Map fields.
-  // This is broken into separate methods depending on whether the value
-  // type is primitive (_ProtobufMap), enum (_ProtobufEnumMap), or message
-  // (_ProtobufMessageMap)
-
-  /// Decode a map whose values are primitive types (including string and bytes)
-  mutating func decodeMapField<KeyType, ValueType: MapValueType>(fieldType: _ProtobufMap<KeyType, ValueType>.Type, value: inout _ProtobufMap<KeyType, ValueType>.BaseType) throws
-  /// Decode a map whose values are protobuf enum types
-  mutating func decodeMapField<KeyType, ValueType>(fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type, value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType) throws where ValueType.RawValue == Int
-  /// Decode a map whose values are protobuf message types
-  mutating func decodeMapField<KeyType, ValueType>(fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type, value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType) throws
-
-  // Decode extension fields
-
-  /// Decode an extension field
-  mutating func decodeExtensionField(values: inout ExtensionFieldValueSet, messageType: any Message.Type, fieldNumber: Int) throws
-
-  // Run a decode loop decoding the MessageSet format for Extensions.
-  mutating func decodeExtensionFieldsAsMessageSet(values: inout ExtensionFieldValueSet,
-                                                  messageType: any Message.Type) throws
+    /// Called by a `oneof` when it already has a value and is being asked to
+    /// accept a new value. Some formats require `oneof` decoding to fail in this
+    /// case.
+    mutating func handleConflictingOneOf() throws
+
+    /// Returns the next field number, or nil when the end of the input is
+    /// reached.
+    ///
+    /// For JSON and text format, the decoder translates the field name to a
+    /// number at this point, based on information it obtained from the message
+    /// when it was initialized.
+    mutating func nextFieldNumber() throws -> Int?
+
+    /// Decode a float value to non-`Optional` field storage
+    mutating func decodeSingularFloatField(value: inout Float) throws
+    /// Decode a float value to `Optional` field storage
+    mutating func decodeSingularFloatField(value: inout Float?) throws
+    /// Decode float values to repeated field storage
+    mutating func decodeRepeatedFloatField(value: inout [Float]) throws
+    /// Decode a double value to non-`Optional` field storage
+    mutating func decodeSingularDoubleField(value: inout Double) throws
+    /// Decode a double value to `Optional` field storage
+    mutating func decodeSingularDoubleField(value: inout Double?) throws
+    /// Decode double values to repeated field storage
+    mutating func decodeRepeatedDoubleField(value: inout [Double]) throws
+    /// Decode an int32 value to non-`Optional` field storage
+    mutating func decodeSingularInt32Field(value: inout Int32) throws
+    /// Decode an int32 value to `Optional` field storage
+    mutating func decodeSingularInt32Field(value: inout Int32?) throws
+    /// Decode int32 values to repeated field storage
+    mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws
+    /// Decode an int64 value to non-`Optional` field storage
+    mutating func decodeSingularInt64Field(value: inout Int64) throws
+    /// Decode an int64 value to `Optional` field storage
+    mutating func decodeSingularInt64Field(value: inout Int64?) throws
+    /// Decode int64 values to repeated field storage
+    mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws
+    /// Decode a uint32 value to non-`Optional` field storage
+    mutating func decodeSingularUInt32Field(value: inout UInt32) throws
+    /// Decode a uint32 value to `Optional` field storage
+    mutating func decodeSingularUInt32Field(value: inout UInt32?) throws
+    /// Decode uint32 values to repeated field storage
+    mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws
+    /// Decode a uint64 value to non-`Optional` field storage
+    mutating func decodeSingularUInt64Field(value: inout UInt64) throws
+    /// Decode a uint64 value to `Optional` field storage
+    mutating func decodeSingularUInt64Field(value: inout UInt64?) throws
+    /// Decode uint64 values to repeated field storage
+    mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws
+    /// Decode an sint32 value to non-`Optional` field storage
+    mutating func decodeSingularSInt32Field(value: inout Int32) throws
+    /// Decode an sint32 value to `Optional` field storage
+    mutating func decodeSingularSInt32Field(value: inout Int32?) throws
+    /// Decode sint32 values to repeated field storage
+    mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws
+    /// Decode an sint64 value to non-`Optional` field storage
+    mutating func decodeSingularSInt64Field(value: inout Int64) throws
+    /// Decode an sint64 value to `Optional` field storage
+    mutating func decodeSingularSInt64Field(value: inout Int64?) throws
+    /// Decode sint64 values to repeated field storage
+    mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws
+    /// Decode a fixed32 value to non-`Optional` field storage
+    mutating func decodeSingularFixed32Field(value: inout UInt32) throws
+    /// Decode a fixed32 value to `Optional` field storage
+    mutating func decodeSingularFixed32Field(value: inout UInt32?) throws
+    /// Decode fixed32 values to repeated field storage
+    mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws
+    /// Decode a fixed64 value to non-`Optional` field storage
+    mutating func decodeSingularFixed64Field(value: inout UInt64) throws
+    /// Decode a fixed64 value to `Optional` field storage
+    mutating func decodeSingularFixed64Field(value: inout UInt64?) throws
+    /// Decode fixed64 values to repeated field storage
+    mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws
+    /// Decode an sfixed32 value to non-`Optional` field storage
+    mutating func decodeSingularSFixed32Field(value: inout Int32) throws
+    /// Decode an sfixed32 value to `Optional` field storage
+    mutating func decodeSingularSFixed32Field(value: inout Int32?) throws
+    /// Decode sfixed32 values to repeated field storage
+    mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws
+    /// Decode an sfixed64 value to non-`Optional` field storage
+    mutating func decodeSingularSFixed64Field(value: inout Int64) throws
+    /// Decode an sfixed64 value to `Optional` field storage
+    mutating func decodeSingularSFixed64Field(value: inout Int64?) throws
+    /// Decode sfixed64 values to repeated field storage
+    mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws
+    /// Decode a bool value to non-`Optional` field storage
+    mutating func decodeSingularBoolField(value: inout Bool) throws
+    /// Decode a bool value to `Optional` field storage
+    mutating func decodeSingularBoolField(value: inout Bool?) throws
+    /// Decode bool values to repeated field storage
+    mutating func decodeRepeatedBoolField(value: inout [Bool]) throws
+    /// Decode a string value to non-`Optional` field storage
+    mutating func decodeSingularStringField(value: inout String) throws
+    /// Decode a string value to `Optional` field storage
+    mutating func decodeSingularStringField(value: inout String?) throws
+    /// Decode string values to repeated field storage
+    mutating func decodeRepeatedStringField(value: inout [String]) throws
+    /// Decode a bytes value to non-`Optional` field storage
+    mutating func decodeSingularBytesField(value: inout Data) throws
+    /// Decode a bytes value to `Optional` field storage
+    mutating func decodeSingularBytesField(value: inout Data?) throws
+    /// Decode bytes values to repeated field storage
+    mutating func decodeRepeatedBytesField(value: inout [Data]) throws
+
+    // Decode Enum fields
+
+    /// Decode an enum value to non-`Optional` field storage
+    mutating func decodeSingularEnumField<E: Enum>(value: inout E) throws where E.RawValue == Int
+    /// Decode an enum value to `Optional` field storage
+    mutating func decodeSingularEnumField<E: Enum>(value: inout E?) throws where E.RawValue == Int
+    /// Decode enum values to repeated field storage
+    mutating func decodeRepeatedEnumField<E: Enum>(value: inout [E]) throws where E.RawValue == Int
+
+    // Decode Message fields
+
+    /// Decode a message value to `Optional` field storage.
+    ///
+    /// Unlike the primitive types, message fields are always stored
+    /// as Swift `Optional` values.
+    mutating func decodeSingularMessageField<M: Message>(value: inout M?) throws
+    /// Decode message values to repeated field storage
+    mutating func decodeRepeatedMessageField<M: Message>(value: inout [M]) throws
+
+    // Decode Group fields
+
+    /// Decode a group value to `Optional` field storage.
+    ///
+    /// Unlike the primitive types, message fields are always stored
+    /// as Swift `Optional` values.
+    /// Note that groups are only used in proto2.
+    mutating func decodeSingularGroupField<G: Message>(value: inout G?) throws
+    /// Decode group values to repeated field storage
+    mutating func decodeRepeatedGroupField<G: Message>(value: inout [G]) throws
+
+    // Decode Map fields.
+    // This is broken into separate methods depending on whether the value
+    // type is primitive (_ProtobufMap), enum (_ProtobufEnumMap), or message
+    // (_ProtobufMessageMap)
+
+    /// Decode a map whose values are primitive types (including string and bytes)
+    mutating func decodeMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMap<KeyType, ValueType>.BaseType
+    ) throws
+    /// Decode a map whose values are protobuf enum types
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
+    ) throws where ValueType.RawValue == Int
+    /// Decode a map whose values are protobuf message types
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
+    ) throws
+
+    // Decode extension fields
+
+    /// Decode an extension field
+    mutating func decodeExtensionField(
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type,
+        fieldNumber: Int
+    ) throws
+
+    // Run a decode loop decoding the MessageSet format for Extensions.
+    mutating func decodeExtensionFieldsAsMessageSet(
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type
+    ) throws
 }
 
 /// Most Decoders won't care about Extension handing as in MessageSet
 /// format, so provide a default implementation simply looping on the
 /// fieldNumbers and feeding through to extension decoding.
 extension Decoder {
-  public mutating func decodeExtensionFieldsAsMessageSet(
-    values: inout ExtensionFieldValueSet,
-    messageType: any Message.Type
-  ) throws {
-    while let fieldNumber = try self.nextFieldNumber() {
-      try self.decodeExtensionField(values: &values,
-                                    messageType: messageType,
-                                    fieldNumber: fieldNumber)
+    public mutating func decodeExtensionFieldsAsMessageSet(
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type
+    ) throws {
+        while let fieldNumber = try self.nextFieldNumber() {
+            try self.decodeExtensionField(
+                values: &values,
+                messageType: messageType,
+                fieldNumber: fieldNumber
+            )
+        }
     }
-  }
 }

+ 7 - 5
Sources/SwiftProtobuf/DoubleParser.swift

@@ -25,16 +25,18 @@ internal class DoubleParser {
     // parse if someone crafts something really long (especially for
     // TextFormat due to overflows (see below)).
     private var work =
-      UnsafeMutableBufferPointer<Int8>.allocate(capacity: 128)
+        UnsafeMutableBufferPointer<Int8>.allocate(capacity: 128)
 
     deinit {
         work.deallocate()
     }
 
-    func utf8ToDouble(bytes: UnsafeRawBufferPointer,
-                      start: UnsafeRawBufferPointer.Index,
-                      end: UnsafeRawBufferPointer.Index) -> Double? {
-        return utf8ToDouble(bytes: UnsafeRawBufferPointer(rebasing: bytes[start..<end]))
+    func utf8ToDouble(
+        bytes: UnsafeRawBufferPointer,
+        start: UnsafeRawBufferPointer.Index,
+        end: UnsafeRawBufferPointer.Index
+    ) -> Double? {
+        utf8ToDouble(bytes: UnsafeRawBufferPointer(rebasing: bytes[start..<end]))
     }
 
     func utf8ToDouble(bytes: UnsafeRawBufferPointer, finiteOnly: Bool = true) -> Double? {

+ 58 - 56
Sources/SwiftProtobuf/Enum.swift

@@ -18,71 +18,73 @@
 /// Generated enum types conform to this protocol.
 @preconcurrency
 public protocol Enum: RawRepresentable, Hashable, Sendable {
-  /// Creates a new instance of the enum initialized to its default value.
-  init()
+    /// Creates a new instance of the enum initialized to its default value.
+    init()
 
-  /// Creates a new instance of the enum from the given raw integer value.
-  ///
-  /// For proto2 enums, this initializer will fail if the raw value does not
-  /// correspond to a valid enum value. For proto3 enums, this initializer never
-  /// fails; unknown values are created as instances of the `UNRECOGNIZED` case.
-  ///
-  /// - Parameter rawValue: The raw integer value from which to create the enum
-  ///   value.
-  init?(rawValue: Int)
+    /// Creates a new instance of the enum from the given raw integer value.
+    ///
+    /// For proto2 enums, this initializer will fail if the raw value does not
+    /// correspond to a valid enum value. For proto3 enums, this initializer never
+    /// fails; unknown values are created as instances of the `UNRECOGNIZED` case.
+    ///
+    /// - Parameter rawValue: The raw integer value from which to create the enum
+    ///   value.
+    init?(rawValue: Int)
 
-  /// The raw integer value of the enum value.
-  ///
-  /// For a recognized enum case, this is the integer value of the case as
-  /// defined in the .proto file. For `UNRECOGNIZED` cases in proto3, this is
-  /// the value that was originally decoded.
-  var rawValue: Int { get }
+    /// The raw integer value of the enum value.
+    ///
+    /// For a recognized enum case, this is the integer value of the case as
+    /// defined in the .proto file. For `UNRECOGNIZED` cases in proto3, this is
+    /// the value that was originally decoded.
+    var rawValue: Int { get }
 }
 
 extension Enum {
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(rawValue)
-  }
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(rawValue)
+    }
 
-  /// Internal convenience property representing the name of the enum value (or
-  /// `nil` if it is an `UNRECOGNIZED` value or doesn't provide names).
-  ///
-  /// Since the text format and JSON names are always identical, we don't need
-  /// to distinguish them.
-  internal var name: _NameMap.Name? {
-    guard let nameProviding = Self.self as? any _ProtoNameProviding.Type else {
-      return nil
+    /// Internal convenience property representing the name of the enum value (or
+    /// `nil` if it is an `UNRECOGNIZED` value or doesn't provide names).
+    ///
+    /// Since the text format and JSON names are always identical, we don't need
+    /// to distinguish them.
+    internal var name: _NameMap.Name? {
+        guard let nameProviding = Self.self as? any _ProtoNameProviding.Type else {
+            return nil
+        }
+        return nameProviding._protobuf_nameMap.names(for: rawValue)?.proto
     }
-    return nameProviding._protobuf_nameMap.names(for: rawValue)?.proto
-  }
 
-  /// Internal convenience initializer that returns the enum value with the
-  /// given name, if it provides names.
-  ///
-  /// Since the text format and JSON names are always identical, we don't need
-  /// to distinguish them.
-  ///
-  /// - Parameter name: The name of the enum case.
-  internal init?(name: String) {
-    guard let nameProviding = Self.self as? any _ProtoNameProviding.Type,
-      let number = nameProviding._protobuf_nameMap.number(forJSONName: name) else {
-      return nil
+    /// Internal convenience initializer that returns the enum value with the
+    /// given name, if it provides names.
+    ///
+    /// Since the text format and JSON names are always identical, we don't need
+    /// to distinguish them.
+    ///
+    /// - Parameter name: The name of the enum case.
+    internal init?(name: String) {
+        guard let nameProviding = Self.self as? any _ProtoNameProviding.Type,
+            let number = nameProviding._protobuf_nameMap.number(forJSONName: name)
+        else {
+            return nil
+        }
+        self.init(rawValue: number)
     }
-    self.init(rawValue: number)
-  }
 
-  /// Internal convenience initializer that returns the enum value with the
-  /// given name, if it provides names.
-  ///
-  /// Since the text format and JSON names are always identical, we don't need
-  /// to distinguish them.
-  ///
-  /// - Parameter name: Buffer holding the UTF-8 bytes of the desired name.
-  internal init?(rawUTF8: UnsafeRawBufferPointer) {
-    guard let nameProviding = Self.self as? any _ProtoNameProviding.Type,
-      let number = nameProviding._protobuf_nameMap.number(forJSONName: rawUTF8) else {
-      return nil
+    /// Internal convenience initializer that returns the enum value with the
+    /// given name, if it provides names.
+    ///
+    /// Since the text format and JSON names are always identical, we don't need
+    /// to distinguish them.
+    ///
+    /// - Parameter name: Buffer holding the UTF-8 bytes of the desired name.
+    internal init?(rawUTF8: UnsafeRawBufferPointer) {
+        guard let nameProviding = Self.self as? any _ProtoNameProviding.Type,
+            let number = nameProviding._protobuf_nameMap.number(forJSONName: rawUTF8)
+        else {
+            return nil
+        }
+        self.init(rawValue: number)
     }
-    self.init(rawValue: number)
-  }
 }

+ 10 - 5
Sources/SwiftProtobuf/ExtensibleMessage.swift

@@ -25,13 +25,13 @@ extension ExtensibleMessage {
 
     public func getExtensionValue<F: ExtensionField>(ext: MessageExtension<F, Self>) -> F.ValueType? {
         if let fieldValue = _protobuf_extensionFieldValues[ext.fieldNumber] as? F {
-          return fieldValue.value
+            return fieldValue.value
         }
         return nil
     }
 
     public func hasExtensionValue<F: ExtensionField>(ext: MessageExtension<F, Self>) -> Bool {
-        return _protobuf_extensionFieldValues[ext.fieldNumber] is F
+        _protobuf_extensionFieldValues[ext.fieldNumber] is F
     }
 
     public mutating func clearExtensionValue<F: ExtensionField>(ext: MessageExtension<F, Self>) {
@@ -42,12 +42,16 @@ extension ExtensibleMessage {
 // Additional specializations for the different types of repeated fields so
 // setting them to an empty array clears them from the map.
 extension ExtensibleMessage {
-    public mutating func setExtensionValue<T>(ext: MessageExtension<RepeatedExtensionField<T>, Self>, value: [T.BaseType]) {
+    public mutating func setExtensionValue<T>(
+        ext: MessageExtension<RepeatedExtensionField<T>, Self>,
+        value: [T.BaseType]
+    ) {
         _protobuf_extensionFieldValues[ext.fieldNumber] =
             value.isEmpty ? nil : RepeatedExtensionField<T>(protobufExtension: ext, value: value)
     }
 
-    public mutating func setExtensionValue<T>(ext: MessageExtension<PackedExtensionField<T>, Self>, value: [T.BaseType]) {
+    public mutating func setExtensionValue<T>(ext: MessageExtension<PackedExtensionField<T>, Self>, value: [T.BaseType])
+    {
         _protobuf_extensionFieldValues[ext.fieldNumber] =
             value.isEmpty ? nil : PackedExtensionField<T>(protobufExtension: ext, value: value)
     }
@@ -62,7 +66,8 @@ extension ExtensibleMessage {
             value.isEmpty ? nil : PackedEnumExtensionField<E>(protobufExtension: ext, value: value)
     }
 
-    public mutating func setExtensionValue<M>(ext: MessageExtension<RepeatedMessageExtensionField<M>, Self>, value: [M]) {
+    public mutating func setExtensionValue<M>(ext: MessageExtension<RepeatedMessageExtensionField<M>, Self>, value: [M])
+    {
         _protobuf_extensionFieldValues[ext.fieldNumber] =
             value.isEmpty ? nil : RepeatedMessageExtensionField<M>(protobufExtension: ext, value: value)
     }

+ 58 - 53
Sources/SwiftProtobuf/ExtensionFieldValueSet.swift

@@ -15,70 +15,75 @@
 // -----------------------------------------------------------------------------
 
 public struct ExtensionFieldValueSet: Hashable, Sendable {
-  fileprivate var values = [Int : any AnyExtensionField]()
+    fileprivate var values = [Int: any AnyExtensionField]()
 
-  public static func ==(lhs: ExtensionFieldValueSet,
-                        rhs: ExtensionFieldValueSet) -> Bool {
-    guard lhs.values.count == rhs.values.count else {
-      return false
-    }
-    for (index, l) in lhs.values {
-      if let r = rhs.values[index] {
-        if type(of: l) != type(of: r) {
-          return false
+    public static func == (
+        lhs: ExtensionFieldValueSet,
+        rhs: ExtensionFieldValueSet
+    ) -> Bool {
+        guard lhs.values.count == rhs.values.count else {
+            return false
         }
-        if !l.isEqual(other: r) {
-          return false
+        for (index, l) in lhs.values {
+            if let r = rhs.values[index] {
+                if type(of: l) != type(of: r) {
+                    return false
+                }
+                if !l.isEqual(other: r) {
+                    return false
+                }
+            } else {
+                return false
+            }
         }
-      } else {
-        return false
-      }
+        return true
     }
-    return true
-  }
 
-  public init() {}
+    public init() {}
 
-  public func hash(into hasher: inout Hasher) {
-    // AnyExtensionField is not Hashable, and the Self constraint that would
-    // add breaks some of the uses of it; so the only choice is to manually
-    // mix things in. However, one must remember to do things in an order
-    // independent manner.
-    var hash = 16777619
-    for (fieldNumber, v) in values {
-      var localHasher = hasher
-      localHasher.combine(fieldNumber)
-      v.hash(into: &localHasher)
-      hash = hash &+ localHasher.finalize()
+    public func hash(into hasher: inout Hasher) {
+        // AnyExtensionField is not Hashable, and the Self constraint that would
+        // add breaks some of the uses of it; so the only choice is to manually
+        // mix things in. However, one must remember to do things in an order
+        // independent manner.
+        var hash = 16_777_619
+        for (fieldNumber, v) in values {
+            var localHasher = hasher
+            localHasher.combine(fieldNumber)
+            v.hash(into: &localHasher)
+            hash = hash &+ localHasher.finalize()
+        }
+        hasher.combine(hash)
     }
-    hasher.combine(hash)
-  }
 
-  public func traverse<V: Visitor>(visitor: inout V, start: Int, end: Int) throws {
-    let validIndexes = values.keys.filter {$0 >= start && $0 < end}
-    for i in validIndexes.sorted() {
-      let value = values[i]!
-      try value.traverse(visitor: &visitor)
+    public func traverse<V: Visitor>(visitor: inout V, start: Int, end: Int) throws {
+        let validIndexes = values.keys.filter { $0 >= start && $0 < end }
+        for i in validIndexes.sorted() {
+            let value = values[i]!
+            try value.traverse(visitor: &visitor)
+        }
     }
-  }
 
-  public subscript(index: Int) -> (any AnyExtensionField)? {
-    get { return values[index] }
-    set { values[index] = newValue }
-  }
+    public subscript(index: Int) -> (any AnyExtensionField)? {
+        get { values[index] }
+        set { values[index] = newValue }
+    }
 
-  mutating func modify<ReturnType>(index: Int, _ modifier: (inout (any AnyExtensionField)?) throws -> ReturnType) rethrows -> ReturnType {
-    // This internal helper exists to invoke the _modify accessor on Dictionary for the given operation, which can avoid CoWs
-    // during the modification operation.
-    return try modifier(&values[index])
-  }
+    mutating func modify<ReturnType>(
+        index: Int,
+        _ modifier: (inout (any AnyExtensionField)?) throws -> ReturnType
+    ) rethrows -> ReturnType {
+        // This internal helper exists to invoke the _modify accessor on Dictionary for the given operation, which can avoid CoWs
+        // during the modification operation.
+        try modifier(&values[index])
+    }
 
-  public var isInitialized: Bool {
-    for (_, v) in values {
-      if !v.isInitialized {
-        return false
-      }
+    public var isInitialized: Bool {
+        for (_, v) in values {
+            if !v.isInitialized {
+                return false
+            }
+        }
+        return true
     }
-    return true
-  }
 }

+ 577 - 542
Sources/SwiftProtobuf/ExtensionFields.swift

@@ -24,24 +24,24 @@
 //
 @preconcurrency
 public protocol AnyExtensionField: Sendable, CustomDebugStringConvertible {
-  func hash(into hasher: inout Hasher)
-  var protobufExtension: any AnyMessageExtension { get }
-  func isEqual(other: any AnyExtensionField) -> Bool
+    func hash(into hasher: inout Hasher)
+    var protobufExtension: any AnyMessageExtension { get }
+    func isEqual(other: any AnyExtensionField) -> Bool
 
-  /// Merging field decoding
-  mutating func decodeExtensionField<T: Decoder>(decoder: inout T) throws
+    /// Merging field decoding
+    mutating func decodeExtensionField<T: Decoder>(decoder: inout T) throws
 
-  /// Fields know their own type, so can dispatch to a visitor
-  func traverse<V: Visitor>(visitor: inout V) throws
+    /// Fields know their own type, so can dispatch to a visitor
+    func traverse<V: Visitor>(visitor: inout V) throws
 
-  /// Check if the field is initialized.
-  var isInitialized: Bool { get }
+    /// Check if the field is initialized.
+    var isInitialized: Bool { get }
 }
 
 extension AnyExtensionField {
-  // Default implementation for extensions fields.  The message types below provide
-  // custom versions.
-  public var isInitialized: Bool { return true }
+    // Default implementation for extensions fields.  The message types below provide
+    // custom versions.
+    public var isInitialized: Bool { true }
 }
 
 ///
@@ -49,122 +49,126 @@ extension AnyExtensionField {
 ///
 @preconcurrency
 public protocol ExtensionField: AnyExtensionField, Hashable {
-  associatedtype ValueType
-  var value: ValueType { get set }
-  init(protobufExtension: any AnyMessageExtension, value: ValueType)
-  init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws
+    associatedtype ValueType
+    var value: ValueType { get set }
+    init(protobufExtension: any AnyMessageExtension, value: ValueType)
+    init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws
 }
 
 ///
 /// Singular field
 ///
 public struct OptionalExtensionField<T: FieldType>: ExtensionField {
-  public typealias BaseType = T.BaseType
-  public typealias ValueType = BaseType
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: OptionalExtensionField,
-                        rhs: OptionalExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return String(reflecting: value)
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! OptionalExtensionField<T>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-      var v: ValueType?
-      try T.decodeSingular(value: &v, from: &decoder)
-      if let v = v {
-          value = v
-      }
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType?
-    try T.decodeSingular(value: &v, from: &decoder)
-    if let v = v {
-      self.init(protobufExtension: protobufExtension, value: v)
-    } else {
-      return nil
-    }
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    try T.visitSingular(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor)
-  }
+    public typealias BaseType = T.BaseType
+    public typealias ValueType = BaseType
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: OptionalExtensionField,
+        rhs: OptionalExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return String(reflecting: value)
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! OptionalExtensionField<T>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        var v: ValueType?
+        try T.decodeSingular(value: &v, from: &decoder)
+        if let v = v {
+            value = v
+        }
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType?
+        try T.decodeSingular(value: &v, from: &decoder)
+        if let v = v {
+            self.init(protobufExtension: protobufExtension, value: v)
+        } else {
+            return nil
+        }
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        try T.visitSingular(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor)
+    }
 }
 
 ///
 /// Repeated fields
 ///
 public struct RepeatedExtensionField<T: FieldType>: ExtensionField {
-  public typealias BaseType = T.BaseType
-  public typealias ValueType = [BaseType]
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: RepeatedExtensionField,
-                        rhs: RepeatedExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]"
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! RepeatedExtensionField<T>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    try T.decodeRepeated(value: &value, from: &decoder)
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType = []
-    try T.decodeRepeated(value: &v, from: &decoder)
-    self.init(protobufExtension: protobufExtension, value: v)
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    if value.count > 0 {
-      try T.visitRepeated(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor)
-    }
-  }
+    public typealias BaseType = T.BaseType
+    public typealias ValueType = [BaseType]
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: RepeatedExtensionField,
+        rhs: RepeatedExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]"
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! RepeatedExtensionField<T>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        try T.decodeRepeated(value: &value, from: &decoder)
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType = []
+        try T.decodeRepeated(value: &v, from: &decoder)
+        self.init(protobufExtension: protobufExtension, value: v)
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        if value.count > 0 {
+            try T.visitRepeated(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor)
+        }
+    }
 }
 
 ///
@@ -174,169 +178,177 @@ public struct RepeatedExtensionField<T: FieldType>: ExtensionField {
 /// find a way to collapse the implementations.
 ///
 public struct PackedExtensionField<T: FieldType>: ExtensionField {
-  public typealias BaseType = T.BaseType
-  public typealias ValueType = [BaseType]
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: PackedExtensionField,
-                        rhs: PackedExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]"
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! PackedExtensionField<T>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    try T.decodeRepeated(value: &value, from: &decoder)
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType = []
-    try T.decodeRepeated(value: &v, from: &decoder)
-    self.init(protobufExtension: protobufExtension, value: v)
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    if value.count > 0 {
-      try T.visitPacked(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor)
-    }
-  }
+    public typealias BaseType = T.BaseType
+    public typealias ValueType = [BaseType]
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: PackedExtensionField,
+        rhs: PackedExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]"
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! PackedExtensionField<T>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        try T.decodeRepeated(value: &value, from: &decoder)
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType = []
+        try T.decodeRepeated(value: &v, from: &decoder)
+        self.init(protobufExtension: protobufExtension, value: v)
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        if value.count > 0 {
+            try T.visitPacked(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor)
+        }
+    }
 }
 
 ///
 /// Enum extensions
 ///
 public struct OptionalEnumExtensionField<E: Enum>: ExtensionField where E.RawValue == Int {
-  public typealias BaseType = E
-  public typealias ValueType = E
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: OptionalEnumExtensionField,
-                        rhs: OptionalEnumExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return String(reflecting: value)
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! OptionalEnumExtensionField<E>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-      var v: ValueType?
-      try decoder.decodeSingularEnumField(value: &v)
-      if let v = v {
-          value = v
-      }
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType?
-    try decoder.decodeSingularEnumField(value: &v)
-    if let v = v {
-      self.init(protobufExtension: protobufExtension, value: v)
-    } else {
-      return nil
-    }
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    try visitor.visitSingularEnumField(
-      value: value,
-      fieldNumber: protobufExtension.fieldNumber)
-  }
+    public typealias BaseType = E
+    public typealias ValueType = E
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: OptionalEnumExtensionField,
+        rhs: OptionalEnumExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return String(reflecting: value)
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! OptionalEnumExtensionField<E>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        var v: ValueType?
+        try decoder.decodeSingularEnumField(value: &v)
+        if let v = v {
+            value = v
+        }
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType?
+        try decoder.decodeSingularEnumField(value: &v)
+        if let v = v {
+            self.init(protobufExtension: protobufExtension, value: v)
+        } else {
+            return nil
+        }
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        try visitor.visitSingularEnumField(
+            value: value,
+            fieldNumber: protobufExtension.fieldNumber
+        )
+    }
 }
 
 ///
 /// Repeated Enum fields
 ///
 public struct RepeatedEnumExtensionField<E: Enum>: ExtensionField where E.RawValue == Int {
-  public typealias BaseType = E
-  public typealias ValueType = [E]
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: RepeatedEnumExtensionField,
-                        rhs: RepeatedEnumExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]"
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! RepeatedEnumExtensionField<E>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    try decoder.decodeRepeatedEnumField(value: &value)
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType = []
-    try decoder.decodeRepeatedEnumField(value: &v)
-    self.init(protobufExtension: protobufExtension, value: v)
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    if value.count > 0 {
-      try visitor.visitRepeatedEnumField(
-        value: value,
-        fieldNumber: protobufExtension.fieldNumber)
-    }
-  }
+    public typealias BaseType = E
+    public typealias ValueType = [E]
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: RepeatedEnumExtensionField,
+        rhs: RepeatedEnumExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]"
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! RepeatedEnumExtensionField<E>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        try decoder.decodeRepeatedEnumField(value: &value)
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType = []
+        try decoder.decodeRepeatedEnumField(value: &v)
+        self.init(protobufExtension: protobufExtension, value: v)
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        if value.count > 0 {
+            try visitor.visitRepeatedEnumField(
+                value: value,
+                fieldNumber: protobufExtension.fieldNumber
+            )
+        }
+    }
 }
 
 ///
@@ -346,178 +358,191 @@ public struct RepeatedEnumExtensionField<E: Enum>: ExtensionField where E.RawVal
 /// find a way to collapse the implementations.
 ///
 public struct PackedEnumExtensionField<E: Enum>: ExtensionField where E.RawValue == Int {
-  public typealias BaseType = E
-  public typealias ValueType = [E]
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: PackedEnumExtensionField,
-                        rhs: PackedEnumExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]"
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! PackedEnumExtensionField<E>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    try decoder.decodeRepeatedEnumField(value: &value)
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType = []
-    try decoder.decodeRepeatedEnumField(value: &v)
-    self.init(protobufExtension: protobufExtension, value: v)
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    if value.count > 0 {
-      try visitor.visitPackedEnumField(
-        value: value,
-        fieldNumber: protobufExtension.fieldNumber)
-    }
-  }
+    public typealias BaseType = E
+    public typealias ValueType = [E]
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: PackedEnumExtensionField,
+        rhs: PackedEnumExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]"
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! PackedEnumExtensionField<E>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        try decoder.decodeRepeatedEnumField(value: &value)
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType = []
+        try decoder.decodeRepeatedEnumField(value: &v)
+        self.init(protobufExtension: protobufExtension, value: v)
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        if value.count > 0 {
+            try visitor.visitPackedEnumField(
+                value: value,
+                fieldNumber: protobufExtension.fieldNumber
+            )
+        }
+    }
 }
 
 //
 // ========== Message ==========
 //
 public struct OptionalMessageExtensionField<M: Message & Equatable>:
-  ExtensionField {
-  public typealias BaseType = M
-  public typealias ValueType = BaseType
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: OptionalMessageExtensionField,
-                        rhs: OptionalMessageExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return String(reflecting: value)
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    value.hash(into: &hasher)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! OptionalMessageExtensionField<M>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    var v: ValueType? = value
-    try decoder.decodeSingularMessageField(value: &v)
-    if let v = v {
-      self.value = v
-    }
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType?
-    try decoder.decodeSingularMessageField(value: &v)
-    if let v = v {
-      self.init(protobufExtension: protobufExtension, value: v)
-    } else {
-      return nil
-    }
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    try visitor.visitSingularMessageField(
-      value: value, fieldNumber: protobufExtension.fieldNumber)
-  }
-
-  public var isInitialized: Bool {
-    return value.isInitialized
-  }
+    ExtensionField
+{
+    public typealias BaseType = M
+    public typealias ValueType = BaseType
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: OptionalMessageExtensionField,
+        rhs: OptionalMessageExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return String(reflecting: value)
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        value.hash(into: &hasher)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! OptionalMessageExtensionField<M>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        var v: ValueType? = value
+        try decoder.decodeSingularMessageField(value: &v)
+        if let v = v {
+            self.value = v
+        }
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType?
+        try decoder.decodeSingularMessageField(value: &v)
+        if let v = v {
+            self.init(protobufExtension: protobufExtension, value: v)
+        } else {
+            return nil
+        }
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        try visitor.visitSingularMessageField(
+            value: value,
+            fieldNumber: protobufExtension.fieldNumber
+        )
+    }
+
+    public var isInitialized: Bool {
+        value.isInitialized
+    }
 }
 
 public struct RepeatedMessageExtensionField<M: Message & Equatable>:
-  ExtensionField {
-  public typealias BaseType = M
-  public typealias ValueType = [BaseType]
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: RepeatedMessageExtensionField,
-                        rhs: RepeatedMessageExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]"
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    for e in value {
-      e.hash(into: &hasher)
-    }
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! RepeatedMessageExtensionField<M>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    try decoder.decodeRepeatedMessageField(value: &value)
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType = []
-    try decoder.decodeRepeatedMessageField(value: &v)
-    self.init(protobufExtension: protobufExtension, value: v)
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    if value.count > 0 {
-      try visitor.visitRepeatedMessageField(
-        value: value, fieldNumber: protobufExtension.fieldNumber)
-    }
-  }
-
-  public var isInitialized: Bool {
-    return Internal.areAllInitialized(value)
-  }
+    ExtensionField
+{
+    public typealias BaseType = M
+    public typealias ValueType = [BaseType]
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: RepeatedMessageExtensionField,
+        rhs: RepeatedMessageExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]"
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        for e in value {
+            e.hash(into: &hasher)
+        }
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! RepeatedMessageExtensionField<M>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        try decoder.decodeRepeatedMessageField(value: &value)
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType = []
+        try decoder.decodeRepeatedMessageField(value: &v)
+        self.init(protobufExtension: protobufExtension, value: v)
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        if value.count > 0 {
+            try visitor.visitRepeatedMessageField(
+                value: value,
+                fieldNumber: protobufExtension.fieldNumber
+            )
+        }
+    }
+
+    public var isInitialized: Bool {
+        Internal.areAllInitialized(value)
+    }
 }
 
 //
@@ -527,119 +552,129 @@ public struct RepeatedMessageExtensionField<M: Message & Equatable>:
 // they serialize very differently, so we have separate serialization
 // handling here...
 public struct OptionalGroupExtensionField<G: Message & Hashable>:
-  ExtensionField {
-  public typealias BaseType = G
-  public typealias ValueType = BaseType
-  public var value: G
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: OptionalGroupExtensionField,
-                        rhs: OptionalGroupExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return value.debugDescription
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! OptionalGroupExtensionField<G>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    var v: ValueType? = value
-    try decoder.decodeSingularGroupField(value: &v)
-    if let v = v {
-      value = v
-    }
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType?
-    try decoder.decodeSingularGroupField(value: &v)
-    if let v = v {
-      self.init(protobufExtension: protobufExtension, value: v)
-    } else {
-      return nil
-    }
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    try visitor.visitSingularGroupField(
-      value: value, fieldNumber: protobufExtension.fieldNumber)
-  }
-
-  public var isInitialized: Bool {
-    return value.isInitialized
-  }
+    ExtensionField
+{
+    public typealias BaseType = G
+    public typealias ValueType = BaseType
+    public var value: G
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: OptionalGroupExtensionField,
+        rhs: OptionalGroupExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return value.debugDescription
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! OptionalGroupExtensionField<G>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        var v: ValueType? = value
+        try decoder.decodeSingularGroupField(value: &v)
+        if let v = v {
+            value = v
+        }
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType?
+        try decoder.decodeSingularGroupField(value: &v)
+        if let v = v {
+            self.init(protobufExtension: protobufExtension, value: v)
+        } else {
+            return nil
+        }
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        try visitor.visitSingularGroupField(
+            value: value,
+            fieldNumber: protobufExtension.fieldNumber
+        )
+    }
+
+    public var isInitialized: Bool {
+        value.isInitialized
+    }
 }
 
 public struct RepeatedGroupExtensionField<G: Message & Hashable>:
-  ExtensionField {
-  public typealias BaseType = G
-  public typealias ValueType = [BaseType]
-  public var value: ValueType
-  public var protobufExtension: any AnyMessageExtension
-
-  public static func ==(lhs: RepeatedGroupExtensionField,
-                        rhs: RepeatedGroupExtensionField) -> Bool {
-    return lhs.value == rhs.value
-  }
-
-  public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
-    self.protobufExtension = protobufExtension
-    self.value = value
-  }
-  
-  public var debugDescription: String {
-    #if DEBUG
-    return "[" + value.map{$0.debugDescription}.joined(separator: ",") + "]"
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  public func hash(into hasher: inout Hasher) {
-    hasher.combine(value)
-  }
-
-  public func isEqual(other: any AnyExtensionField) -> Bool {
-    let o = other as! RepeatedGroupExtensionField<G>
-    return self == o
-  }
-
-  public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
-    try decoder.decodeRepeatedGroupField(value: &value)
-  }
-
-  public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
-    var v: ValueType = []
-    try decoder.decodeRepeatedGroupField(value: &v)
-    self.init(protobufExtension: protobufExtension, value: v)
-  }
-
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    if value.count > 0 {
-      try visitor.visitRepeatedGroupField(
-        value: value, fieldNumber: protobufExtension.fieldNumber)
-    }
-  }
-
-  public var isInitialized: Bool {
-    return Internal.areAllInitialized(value)
-  }
+    ExtensionField
+{
+    public typealias BaseType = G
+    public typealias ValueType = [BaseType]
+    public var value: ValueType
+    public var protobufExtension: any AnyMessageExtension
+
+    public static func == (
+        lhs: RepeatedGroupExtensionField,
+        rhs: RepeatedGroupExtensionField
+    ) -> Bool {
+        lhs.value == rhs.value
+    }
+
+    public init(protobufExtension: any AnyMessageExtension, value: ValueType) {
+        self.protobufExtension = protobufExtension
+        self.value = value
+    }
+
+    public var debugDescription: String {
+        #if DEBUG
+        return "[" + value.map { $0.debugDescription }.joined(separator: ",") + "]"
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(value)
+    }
+
+    public func isEqual(other: any AnyExtensionField) -> Bool {
+        let o = other as! RepeatedGroupExtensionField<G>
+        return self == o
+    }
+
+    public mutating func decodeExtensionField<D: Decoder>(decoder: inout D) throws {
+        try decoder.decodeRepeatedGroupField(value: &value)
+    }
+
+    public init?<D: Decoder>(protobufExtension: any AnyMessageExtension, decoder: inout D) throws {
+        var v: ValueType = []
+        try decoder.decodeRepeatedGroupField(value: &v)
+        self.init(protobufExtension: protobufExtension, value: v)
+    }
+
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        if value.count > 0 {
+            try visitor.visitRepeatedGroupField(
+                value: value,
+                fieldNumber: protobufExtension.fieldNumber
+            )
+        }
+    }
+
+    public var isInitialized: Bool {
+        Internal.areAllInitialized(value)
+    }
 }

+ 38 - 38
Sources/SwiftProtobuf/FieldTag.swift

@@ -21,49 +21,49 @@
 /// for validity because they are guaranteed correct at initialization time.
 internal struct FieldTag: RawRepresentable {
 
-  typealias RawValue = UInt32
+    typealias RawValue = UInt32
 
-  /// The raw numeric value of the tag, which contains both the field number and
-  /// wire format.
-  let rawValue: UInt32
+    /// The raw numeric value of the tag, which contains both the field number and
+    /// wire format.
+    let rawValue: UInt32
 
-  /// The field number component of the tag.
-  var fieldNumber: Int {
-    return Int(rawValue >> 3)
-  }
+    /// The field number component of the tag.
+    var fieldNumber: Int {
+        Int(rawValue >> 3)
+    }
 
-  /// The wire format component of the tag.
-  var wireFormat: WireFormat {
-    // This force-unwrap is safe because there are only two initialization
-    // paths: one that takes a WireFormat directly (and is guaranteed valid at
-    // compile-time), or one that takes a raw value but which only lets valid
-    // wire formats through.
-    return WireFormat(rawValue: UInt8(rawValue & 7))!
-  }
+    /// The wire format component of the tag.
+    var wireFormat: WireFormat {
+        // This force-unwrap is safe because there are only two initialization
+        // paths: one that takes a WireFormat directly (and is guaranteed valid at
+        // compile-time), or one that takes a raw value but which only lets valid
+        // wire formats through.
+        WireFormat(rawValue: UInt8(rawValue & 7))!
+    }
 
-  /// A helper property that returns the number of bytes required to
-  /// varint-encode this tag.
-  var encodedSize: Int {
-    return Varint.encodedSize(of: rawValue)
-  }
+    /// A helper property that returns the number of bytes required to
+    /// varint-encode this tag.
+    var encodedSize: Int {
+        Varint.encodedSize(of: rawValue)
+    }
 
-  /// Creates a new tag from its raw numeric representation.
-  ///
-  /// Note that if the raw value given here is not a valid tag (for example, it
-  /// has an invalid wire format), this initializer will fail.
-  init?(rawValue: UInt32) {
-    // Verify that the field number and wire format are valid and fail if they
-    // are not.
-    guard rawValue & ~0x07 != 0,
-      let _ = WireFormat(rawValue: UInt8(rawValue % 8)) else {
-      return nil
+    /// Creates a new tag from its raw numeric representation.
+    ///
+    /// Note that if the raw value given here is not a valid tag (for example, it
+    /// has an invalid wire format), this initializer will fail.
+    init?(rawValue: UInt32) {
+        // Verify that the field number and wire format are valid and fail if they
+        // are not.
+        guard rawValue & ~0x07 != 0,
+            let _ = WireFormat(rawValue: UInt8(rawValue % 8))
+        else {
+            return nil
+        }
+        self.rawValue = rawValue
     }
-    self.rawValue = rawValue
-  }
 
-  /// Creates a new tag by composing the given field number and wire format.
-  init(fieldNumber: Int, wireFormat: WireFormat) {
-    self.rawValue = UInt32(truncatingIfNeeded: fieldNumber) << 3 |
-      UInt32(wireFormat.rawValue)
-  }
+    /// Creates a new tag by composing the given field number and wire format.
+    init(fieldNumber: Int, wireFormat: WireFormat) {
+        self.rawValue = UInt32(truncatingIfNeeded: fieldNumber) << 3 | UInt32(wireFormat.rawValue)
+    }
 }

+ 16 - 16
Sources/SwiftProtobuf/FieldTypes.swift

@@ -61,7 +61,7 @@ public protocol MapKeyType: FieldType {
 // Default impl for anything `Comparable`
 extension MapKeyType where BaseType: Comparable {
     public static func _lessThan(lhs: BaseType, rhs: BaseType) -> Bool {
-        return lhs < rhs
+        lhs < rhs
     }
 }
 
@@ -82,7 +82,7 @@ public protocol MapValueType: FieldType {
 ///
 public struct ProtobufFloat: FieldType, MapValueType {
     public typealias BaseType = Float
-    public static var proto3DefaultValue: Float {return 0.0}
+    public static var proto3DefaultValue: Float { 0.0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularFloatField(value: &value)
     }
@@ -105,7 +105,7 @@ public struct ProtobufFloat: FieldType, MapValueType {
 ///
 public struct ProtobufDouble: FieldType, MapValueType {
     public typealias BaseType = Double
-    public static var proto3DefaultValue: Double {return 0.0}
+    public static var proto3DefaultValue: Double { 0.0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularDoubleField(value: &value)
     }
@@ -128,7 +128,7 @@ public struct ProtobufDouble: FieldType, MapValueType {
 ///
 public struct ProtobufInt32: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = Int32
-    public static var proto3DefaultValue: Int32 {return 0}
+    public static var proto3DefaultValue: Int32 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularInt32Field(value: &value)
     }
@@ -152,7 +152,7 @@ public struct ProtobufInt32: FieldType, MapKeyType, MapValueType {
 
 public struct ProtobufInt64: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = Int64
-    public static var proto3DefaultValue: Int64 {return 0}
+    public static var proto3DefaultValue: Int64 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularInt64Field(value: &value)
     }
@@ -175,7 +175,7 @@ public struct ProtobufInt64: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufUInt32: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = UInt32
-    public static var proto3DefaultValue: UInt32 {return 0}
+    public static var proto3DefaultValue: UInt32 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularUInt32Field(value: &value)
     }
@@ -199,7 +199,7 @@ public struct ProtobufUInt32: FieldType, MapKeyType, MapValueType {
 
 public struct ProtobufUInt64: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = UInt64
-    public static var proto3DefaultValue: UInt64 {return 0}
+    public static var proto3DefaultValue: UInt64 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularUInt64Field(value: &value)
     }
@@ -222,7 +222,7 @@ public struct ProtobufUInt64: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufSInt32: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = Int32
-    public static var proto3DefaultValue: Int32 {return 0}
+    public static var proto3DefaultValue: Int32 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularSInt32Field(value: &value)
     }
@@ -246,7 +246,7 @@ public struct ProtobufSInt32: FieldType, MapKeyType, MapValueType {
 
 public struct ProtobufSInt64: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = Int64
-    public static var proto3DefaultValue: Int64 {return 0}
+    public static var proto3DefaultValue: Int64 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularSInt64Field(value: &value)
     }
@@ -269,7 +269,7 @@ public struct ProtobufSInt64: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufFixed32: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = UInt32
-    public static var proto3DefaultValue: UInt32 {return 0}
+    public static var proto3DefaultValue: UInt32 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularFixed32Field(value: &value)
     }
@@ -292,7 +292,7 @@ public struct ProtobufFixed32: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufFixed64: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = UInt64
-    public static var proto3DefaultValue: UInt64 {return 0}
+    public static var proto3DefaultValue: UInt64 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularFixed64Field(value: &value)
     }
@@ -315,7 +315,7 @@ public struct ProtobufFixed64: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufSFixed32: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = Int32
-    public static var proto3DefaultValue: Int32 {return 0}
+    public static var proto3DefaultValue: Int32 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularSFixed32Field(value: &value)
     }
@@ -338,7 +338,7 @@ public struct ProtobufSFixed32: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufSFixed64: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = Int64
-    public static var proto3DefaultValue: Int64 {return 0}
+    public static var proto3DefaultValue: Int64 { 0 }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularSFixed64Field(value: &value)
     }
@@ -361,7 +361,7 @@ public struct ProtobufSFixed64: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufBool: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = Bool
-    public static var proto3DefaultValue: Bool {return false}
+    public static var proto3DefaultValue: Bool { false }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularBoolField(value: &value)
     }
@@ -392,7 +392,7 @@ public struct ProtobufBool: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufString: FieldType, MapKeyType, MapValueType {
     public typealias BaseType = String
-    public static var proto3DefaultValue: String {return String()}
+    public static var proto3DefaultValue: String { String() }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularStringField(value: &value)
     }
@@ -415,7 +415,7 @@ public struct ProtobufString: FieldType, MapKeyType, MapValueType {
 ///
 public struct ProtobufBytes: FieldType, MapValueType {
     public typealias BaseType = Data
-    public static var proto3DefaultValue: Data {return Data()}
+    public static var proto3DefaultValue: Data { Data() }
     public static func decodeSingular<D: Decoder>(value: inout BaseType?, from decoder: inout D) throws {
         try decoder.decodeSingularBytesField(value: &value)
     }

+ 131 - 128
Sources/SwiftProtobuf/Google_Protobuf_Any+Extensions.swift

@@ -19,150 +19,153 @@ import Foundation
 public let defaultAnyTypeURLPrefix: String = "type.googleapis.com"
 
 extension Google_Protobuf_Any {
-  /// Initialize an Any object from the provided message.
-  ///
-  /// This corresponds to the `pack` operation in the C++ API.
-  ///
-  /// Unlike the C++ implementation, the message is not immediately
-  /// serialized; it is merely stored until the Any object itself
-  /// needs to be serialized.  This design avoids unnecessary
-  /// decoding/recoding when writing JSON format.
-  ///
-  /// - Parameters:
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryEncodingError/missingRequiredFields``.
-  ///   - typePrefix: The prefix to be used when building the `type_url`.
-  ///     Defaults to "type.googleapis.com".
-  /// - Throws: ``BinaryEncodingError/missingRequiredFields`` if 
-  /// `partial` is false and `message` wasn't fully initialized.
-  public init(
-    message: any Message,
-    partial: Bool = false,
-    typePrefix: String = defaultAnyTypeURLPrefix
-  ) throws {
-    if !partial && !message.isInitialized {
-      throw BinaryEncodingError.missingRequiredFields
+    /// Initialize an Any object from the provided message.
+    ///
+    /// This corresponds to the `pack` operation in the C++ API.
+    ///
+    /// Unlike the C++ implementation, the message is not immediately
+    /// serialized; it is merely stored until the Any object itself
+    /// needs to be serialized.  This design avoids unnecessary
+    /// decoding/recoding when writing JSON format.
+    ///
+    /// - Parameters:
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryEncodingError/missingRequiredFields``.
+    ///   - typePrefix: The prefix to be used when building the `type_url`.
+    ///     Defaults to "type.googleapis.com".
+    /// - Throws: ``BinaryEncodingError/missingRequiredFields`` if
+    /// `partial` is false and `message` wasn't fully initialized.
+    public init(
+        message: any Message,
+        partial: Bool = false,
+        typePrefix: String = defaultAnyTypeURLPrefix
+    ) throws {
+        if !partial && !message.isInitialized {
+            throw BinaryEncodingError.missingRequiredFields
+        }
+        self.init()
+        typeURL = buildTypeURL(forMessage: message, typePrefix: typePrefix)
+        _storage.state = .message(message)
+    }
+
+    /// Creates a new `Google_Protobuf_Any` by decoding the given string
+    /// containing a serialized message in Protocol Buffer text format.
+    ///
+    /// - Parameters:
+    ///   - textFormatString: The text format string to decode.
+    ///   - extensions: An `ExtensionMap` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    /// - Throws: an instance of `TextFormatDecodingError` on failure.
+    @_disfavoredOverload
+    public init(
+        textFormatString: String,
+        extensions: (any ExtensionMap)? = nil
+    ) throws {
+        // TODO: Remove this api and default the options instead when we do a major release.
+        try self.init(
+            textFormatString: textFormatString,
+            options: TextFormatDecodingOptions(),
+            extensions: extensions
+        )
     }
-    self.init()
-    typeURL = buildTypeURL(forMessage:message, typePrefix: typePrefix)
-    _storage.state = .message(message)
-  }
-  
-  /// Creates a new `Google_Protobuf_Any` by decoding the given string
-  /// containing a serialized message in Protocol Buffer text format.
-  ///
-  /// - Parameters:
-  ///   - textFormatString: The text format string to decode.
-  ///   - extensions: An `ExtensionMap` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  /// - Throws: an instance of `TextFormatDecodingError` on failure.
-  @_disfavoredOverload
-  public init(
-    textFormatString: String,
-    extensions: (any ExtensionMap)? = nil
-  ) throws {
-    // TODO: Remove this api and default the options instead when we do a major release.
-    try self.init(textFormatString: textFormatString,
-                  options: TextFormatDecodingOptions(),
-                  extensions: extensions)
-  }
 
-  /// Creates a new `Google_Protobuf_Any` by decoding the given string
-  /// containing a serialized message in Protocol Buffer text format.
-  ///
-  /// - Parameters:
-  ///   - textFormatString: The text format string to decode.
-  ///   - options: The ``TextFormatDecodingOptions`` to use.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  /// - Throws: ``TextFormatDecodingError`` on failure.
-  public init(
-    textFormatString: String,
-    options: TextFormatDecodingOptions = TextFormatDecodingOptions(),
-    extensions: (any ExtensionMap)? = nil
-  ) throws {
-    self.init()
-    if !textFormatString.isEmpty {
-      if let data = textFormatString.data(using: String.Encoding.utf8) {
-        try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-          if let baseAddress = body.baseAddress, body.count > 0 {
-            var textDecoder = try TextFormatDecoder(
-              messageType: Google_Protobuf_Any.self,
-              utf8Pointer: baseAddress,
-              count: body.count,
-              options: options,
-              extensions: extensions)
-            try decodeTextFormat(decoder: &textDecoder)
-            if !textDecoder.complete {
-              throw TextFormatDecodingError.trailingGarbage
+    /// Creates a new `Google_Protobuf_Any` by decoding the given string
+    /// containing a serialized message in Protocol Buffer text format.
+    ///
+    /// - Parameters:
+    ///   - textFormatString: The text format string to decode.
+    ///   - options: The ``TextFormatDecodingOptions`` to use.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    /// - Throws: ``TextFormatDecodingError`` on failure.
+    public init(
+        textFormatString: String,
+        options: TextFormatDecodingOptions = TextFormatDecodingOptions(),
+        extensions: (any ExtensionMap)? = nil
+    ) throws {
+        self.init()
+        if !textFormatString.isEmpty {
+            if let data = textFormatString.data(using: String.Encoding.utf8) {
+                try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+                    if let baseAddress = body.baseAddress, body.count > 0 {
+                        var textDecoder = try TextFormatDecoder(
+                            messageType: Google_Protobuf_Any.self,
+                            utf8Pointer: baseAddress,
+                            count: body.count,
+                            options: options,
+                            extensions: extensions
+                        )
+                        try decodeTextFormat(decoder: &textDecoder)
+                        if !textDecoder.complete {
+                            throw TextFormatDecodingError.trailingGarbage
+                        }
+                    }
+                }
             }
-          }
         }
-      }
     }
-  }
 
-  /// Returns true if this `Google_Protobuf_Any` message contains the given
-  /// message type.
-  ///
-  /// The check is performed by looking at the passed `Message.Type` and the
-  /// `typeURL` of this message.
-  ///
-  /// - Parameter type: The concrete message type.
-  /// - Returns: True if the receiver contains the given message type.
-  public func isA<M: Message>(_ type: M.Type) -> Bool {
-    return _storage.isA(type)
-  }
+    /// Returns true if this `Google_Protobuf_Any` message contains the given
+    /// message type.
+    ///
+    /// The check is performed by looking at the passed `Message.Type` and the
+    /// `typeURL` of this message.
+    ///
+    /// - Parameter type: The concrete message type.
+    /// - Returns: True if the receiver contains the given message type.
+    public func isA<M: Message>(_ type: M.Type) -> Bool {
+        _storage.isA(type)
+    }
 
-  public func hash(into hasher: inout Hasher) {
-    _storage.hash(into: &hasher)
-  }
+    public func hash(into hasher: inout Hasher) {
+        _storage.hash(into: &hasher)
+    }
 }
 
 extension Google_Protobuf_Any {
-  internal func textTraverse(visitor: inout TextFormatEncodingVisitor) {
-    _storage.textTraverse(visitor: &visitor)
-    try! unknownFields.traverse(visitor: &visitor)
-  }
+    internal func textTraverse(visitor: inout TextFormatEncodingVisitor) {
+        _storage.textTraverse(visitor: &visitor)
+        try! unknownFields.traverse(visitor: &visitor)
+    }
 }
 
 extension Google_Protobuf_Any {
-  // Custom text format decoding support for Any objects.
-  // (Note: This is not a part of any protocol; it's invoked
-  // directly from TextFormatDecoder whenever it sees an attempt
-  // to decode an Any object)
-  internal mutating func decodeTextFormat(
-    decoder: inout TextFormatDecoder
-  ) throws {
-    // First, check if this uses the "verbose" Any encoding.
-    // If it does, and we have the type available, we can
-    // eagerly decode the contained Message object.
-    if let url = try decoder.scanner.nextOptionalAnyURL() {
-      try _uniqueStorage().decodeTextFormat(typeURL: url, decoder: &decoder)
-    } else {
-      // This is not using the specialized encoding, so we can use the
-      // standard path to decode the binary value.
-      // First, clear the fields so we don't waste time re-serializing
-      // the previous contents as this instances get replaced with a
-      // new value (can happen when a field name/number is repeated in
-      // the TextFormat input).
-      self.typeURL = ""
-      self.value = Data()
-      try decodeMessage(decoder: &decoder)
+    // Custom text format decoding support for Any objects.
+    // (Note: This is not a part of any protocol; it's invoked
+    // directly from TextFormatDecoder whenever it sees an attempt
+    // to decode an Any object)
+    internal mutating func decodeTextFormat(
+        decoder: inout TextFormatDecoder
+    ) throws {
+        // First, check if this uses the "verbose" Any encoding.
+        // If it does, and we have the type available, we can
+        // eagerly decode the contained Message object.
+        if let url = try decoder.scanner.nextOptionalAnyURL() {
+            try _uniqueStorage().decodeTextFormat(typeURL: url, decoder: &decoder)
+        } else {
+            // This is not using the specialized encoding, so we can use the
+            // standard path to decode the binary value.
+            // First, clear the fields so we don't waste time re-serializing
+            // the previous contents as this instances get replaced with a
+            // new value (can happen when a field name/number is repeated in
+            // the TextFormat input).
+            self.typeURL = ""
+            self.value = Data()
+            try decodeMessage(decoder: &decoder)
+        }
     }
-  }
 }
 
 extension Google_Protobuf_Any: _CustomJSONCodable {
-  internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    return try _storage.encodedJSONString(options: options)
-  }
+    internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        try _storage.encodedJSONString(options: options)
+    }
 
-  internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    try _uniqueStorage().decodeJSON(from: &decoder)
-  }
+    internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        try _uniqueStorage().decodeJSON(from: &decoder)
+    }
 }

+ 51 - 48
Sources/SwiftProtobuf/Google_Protobuf_Any+Registry.swift

@@ -14,12 +14,15 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
+
 #if !os(WASI)
 #if canImport(Dispatch)
 import Dispatch
-fileprivate let knownTypesQueue =
-    DispatchQueue(label: "org.swift.protobuf.typeRegistry",
-                  attributes: .concurrent)
+private let knownTypesQueue =
+    DispatchQueue(
+        label: "org.swift.protobuf.typeRegistry",
+        attributes: .concurrent
+    )
 #endif
 #endif
 
@@ -27,65 +30,65 @@ fileprivate let knownTypesQueue =
 // the general registry support?
 
 internal func buildTypeURL(forMessage message: any Message, typePrefix: String) -> String {
-  var url = typePrefix
-  let needsSlash = typePrefix.isEmpty || typePrefix.last != "/"
-  if needsSlash {
-    url += "/"
-  }
-  return url + typeName(fromMessage: message)
+    var url = typePrefix
+    let needsSlash = typePrefix.isEmpty || typePrefix.last != "/"
+    if needsSlash {
+        url += "/"
+    }
+    return url + typeName(fromMessage: message)
 }
 
 internal func typeName(fromMessage message: any Message) -> String {
-  let messageType = type(of: message)
-  return messageType.protoMessageName
+    let messageType = type(of: message)
+    return messageType.protoMessageName
 }
 
 internal func typeName(fromURL s: String) -> String {
-  var typeStart = s.startIndex
-  var i = typeStart
-  while i < s.endIndex {
-    let c = s[i]
-    i = s.index(after: i)
-    if c == "/" {
-      typeStart = i
+    var typeStart = s.startIndex
+    var i = typeStart
+    while i < s.endIndex {
+        let c = s[i]
+        i = s.index(after: i)
+        if c == "/" {
+            typeStart = i
+        }
     }
-  }
 
-  return String(s[typeStart..<s.endIndex])
+    return String(s[typeStart..<s.endIndex])
 }
 
 // This is adapted from SwiftNIO so sendable checks don't flag issues with
 // `knownTypes`. Another options would be something like NIO's `LockedValueBox`
 // or moving the entire handling to a Task.
-fileprivate final class UnsafeMutableTransferBox<Wrapped> {
-  var wrappedValue: Wrapped
-  init(_ wrappedValue: Wrapped) {
-    self.wrappedValue = wrappedValue
-  }
+private final class UnsafeMutableTransferBox<Wrapped> {
+    var wrappedValue: Wrapped
+    init(_ wrappedValue: Wrapped) {
+        self.wrappedValue = wrappedValue
+    }
 }
 
 extension UnsafeMutableTransferBox: @unchecked Sendable {}
 
 // All access to this should be done on `knownTypesQueue`.
-fileprivate let knownTypes: UnsafeMutableTransferBox<[String:any Message.Type]> = .init([
-  // Seeded with the Well Known Types.
-  "google.protobuf.Any": Google_Protobuf_Any.self,
-  "google.protobuf.BoolValue": Google_Protobuf_BoolValue.self,
-  "google.protobuf.BytesValue": Google_Protobuf_BytesValue.self,
-  "google.protobuf.DoubleValue": Google_Protobuf_DoubleValue.self,
-  "google.protobuf.Duration": Google_Protobuf_Duration.self,
-  "google.protobuf.Empty": Google_Protobuf_Empty.self,
-  "google.protobuf.FieldMask": Google_Protobuf_FieldMask.self,
-  "google.protobuf.FloatValue": Google_Protobuf_FloatValue.self,
-  "google.protobuf.Int32Value": Google_Protobuf_Int32Value.self,
-  "google.protobuf.Int64Value": Google_Protobuf_Int64Value.self,
-  "google.protobuf.ListValue": Google_Protobuf_ListValue.self,
-  "google.protobuf.StringValue": Google_Protobuf_StringValue.self,
-  "google.protobuf.Struct": Google_Protobuf_Struct.self,
-  "google.protobuf.Timestamp": Google_Protobuf_Timestamp.self,
-  "google.protobuf.UInt32Value": Google_Protobuf_UInt32Value.self,
-  "google.protobuf.UInt64Value": Google_Protobuf_UInt64Value.self,
-  "google.protobuf.Value": Google_Protobuf_Value.self,
+private let knownTypes: UnsafeMutableTransferBox<[String: any Message.Type]> = .init([
+    // Seeded with the Well Known Types.
+    "google.protobuf.Any": Google_Protobuf_Any.self,
+    "google.protobuf.BoolValue": Google_Protobuf_BoolValue.self,
+    "google.protobuf.BytesValue": Google_Protobuf_BytesValue.self,
+    "google.protobuf.DoubleValue": Google_Protobuf_DoubleValue.self,
+    "google.protobuf.Duration": Google_Protobuf_Duration.self,
+    "google.protobuf.Empty": Google_Protobuf_Empty.self,
+    "google.protobuf.FieldMask": Google_Protobuf_FieldMask.self,
+    "google.protobuf.FloatValue": Google_Protobuf_FloatValue.self,
+    "google.protobuf.Int32Value": Google_Protobuf_Int32Value.self,
+    "google.protobuf.Int64Value": Google_Protobuf_Int64Value.self,
+    "google.protobuf.ListValue": Google_Protobuf_ListValue.self,
+    "google.protobuf.StringValue": Google_Protobuf_StringValue.self,
+    "google.protobuf.Struct": Google_Protobuf_Struct.self,
+    "google.protobuf.Timestamp": Google_Protobuf_Timestamp.self,
+    "google.protobuf.UInt32Value": Google_Protobuf_UInt32Value.self,
+    "google.protobuf.UInt64Value": Google_Protobuf_UInt64Value.self,
+    "google.protobuf.Value": Google_Protobuf_Value.self,
 ])
 
 extension Google_Protobuf_Any {
@@ -137,8 +140,8 @@ extension Google_Protobuf_Any {
 
     /// Returns the Message.Type expected for the given type URL.
     public static func messageType(forTypeURL url: String) -> (any Message.Type)? {
-      let messageTypeName = typeName(fromURL: url)
-      return messageType(forMessageName: messageTypeName)
+        let messageTypeName = typeName(fromURL: url)
+        return messageType(forMessageName: messageTypeName)
     }
 
     /// Returns the Message.Type expected for the given proto message name.
@@ -152,12 +155,12 @@ extension Google_Protobuf_Any {
 
 }
 
-fileprivate enum DispatchFlags {
+private enum DispatchFlags {
     case barrier
     case none
 }
 
-fileprivate func execute(flags: DispatchFlags, _ closure: () -> Void) {
+private func execute(flags: DispatchFlags, _ closure: () -> Void) {
     #if !os(WASI)
     switch flags {
     case .barrier:

+ 180 - 170
Sources/SwiftProtobuf/Google_Protobuf_Duration+Extensions.swift

@@ -17,214 +17,224 @@
 import Foundation
 
 private let minDurationSeconds: Int64 = -maxDurationSeconds
-private let maxDurationSeconds: Int64 = 315576000000
+private let maxDurationSeconds: Int64 = 315_576_000_000
 
 private func parseDuration(text: String) throws -> (Int64, Int32) {
-  var digits = [Character]()
-  var digitCount = 0
-  var total = 0
-  var chars = text.makeIterator()
-  var seconds: Int64?
-  var nanos: Int32 = 0
-  var isNegative = false
-  while let c = chars.next() {
-    switch c {
-    case "-":
-      // Only accept '-' as very first character
-      if total > 0 {
-        throw JSONDecodingError.malformedDuration
-      }
-      digits.append(c)
-      isNegative = true
-    case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
-      digits.append(c)
-      digitCount += 1
-    case ".":
-      if let _ = seconds {
-        throw JSONDecodingError.malformedDuration
-      }
-      let digitString = String(digits)
-      if let s = Int64(digitString),
-        s >= minDurationSeconds && s <= maxDurationSeconds {
-        seconds = s
-      } else {
-        throw JSONDecodingError.malformedDuration
-      }
-      digits.removeAll()
-      digitCount = 0
-    case "s":
-      if let _ = seconds {
-        // Seconds already set, digits holds nanos
-        while (digitCount < 9) {
-          digits.append(Character("0"))
-          digitCount += 1
+    var digits = [Character]()
+    var digitCount = 0
+    var total = 0
+    var chars = text.makeIterator()
+    var seconds: Int64?
+    var nanos: Int32 = 0
+    var isNegative = false
+    while let c = chars.next() {
+        switch c {
+        case "-":
+            // Only accept '-' as very first character
+            if total > 0 {
+                throw JSONDecodingError.malformedDuration
+            }
+            digits.append(c)
+            isNegative = true
+        case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
+            digits.append(c)
+            digitCount += 1
+        case ".":
+            if let _ = seconds {
+                throw JSONDecodingError.malformedDuration
+            }
+            let digitString = String(digits)
+            if let s = Int64(digitString),
+                s >= minDurationSeconds && s <= maxDurationSeconds
+            {
+                seconds = s
+            } else {
+                throw JSONDecodingError.malformedDuration
+            }
+            digits.removeAll()
+            digitCount = 0
+        case "s":
+            if let _ = seconds {
+                // Seconds already set, digits holds nanos
+                while digitCount < 9 {
+                    digits.append(Character("0"))
+                    digitCount += 1
+                }
+                while digitCount > 9 {
+                    digits.removeLast()
+                    digitCount -= 1
+                }
+                let digitString = String(digits)
+                if let rawNanos = Int32(digitString) {
+                    if isNegative {
+                        nanos = -rawNanos
+                    } else {
+                        nanos = rawNanos
+                    }
+                } else {
+                    throw JSONDecodingError.malformedDuration
+                }
+            } else {
+                // No fraction, we just have an integral number of seconds
+                let digitString = String(digits)
+                if let s = Int64(digitString),
+                    s >= minDurationSeconds && s <= maxDurationSeconds
+                {
+                    seconds = s
+                } else {
+                    throw JSONDecodingError.malformedDuration
+                }
+            }
+            // Fail if there are characters after 's'
+            if chars.next() != nil {
+                throw JSONDecodingError.malformedDuration
+            }
+            return (seconds!, nanos)
+        default:
+            throw JSONDecodingError.malformedDuration
         }
-        while digitCount > 9 {
-          digits.removeLast()
-          digitCount -= 1
-        }
-        let digitString = String(digits)
-        if let rawNanos = Int32(digitString) {
-          if isNegative {
-            nanos = -rawNanos
-          } else {
-            nanos = rawNanos
-          }
-        } else {
-          throw JSONDecodingError.malformedDuration
-        }
-      } else {
-        // No fraction, we just have an integral number of seconds
-        let digitString = String(digits)
-        if let s = Int64(digitString),
-          s >= minDurationSeconds && s <= maxDurationSeconds {
-          seconds = s
-        } else {
-          throw JSONDecodingError.malformedDuration
-        }
-      }
-      // Fail if there are characters after 's'
-      if chars.next() != nil {
-        throw JSONDecodingError.malformedDuration
-      }
-      return (seconds!, nanos)
-    default:
-      throw JSONDecodingError.malformedDuration
+        total += 1
     }
-    total += 1
-  }
-  throw JSONDecodingError.malformedDuration
+    throw JSONDecodingError.malformedDuration
 }
 
 private func formatDuration(seconds: Int64, nanos: Int32) -> String? {
-  let (seconds, nanos) = normalizeForDuration(seconds: seconds, nanos: nanos)
-  guard seconds >= minDurationSeconds && seconds <= maxDurationSeconds else {
-    return nil
-  }
-  let nanosString = nanosToString(nanos: nanos) // Includes leading '.' if needed
-  if seconds == 0 && nanos < 0 {
-    return "-0\(nanosString)s"
-  }
-  return "\(seconds)\(nanosString)s"
+    let (seconds, nanos) = normalizeForDuration(seconds: seconds, nanos: nanos)
+    guard seconds >= minDurationSeconds && seconds <= maxDurationSeconds else {
+        return nil
+    }
+    let nanosString = nanosToString(nanos: nanos)  // Includes leading '.' if needed
+    if seconds == 0 && nanos < 0 {
+        return "-0\(nanosString)s"
+    }
+    return "\(seconds)\(nanosString)s"
 }
 
 extension Google_Protobuf_Duration {
-  /// Creates a new `Google_Protobuf_Duration` equal to the given number of
-  /// seconds and nanoseconds.
-  ///
-  /// - Parameter seconds: The number of seconds.
-  /// - Parameter nanos: The number of nanoseconds.
-  public init(seconds: Int64 = 0, nanos: Int32 = 0) {
-    self.init()
-    self.seconds = seconds
-    self.nanos = nanos
-  }
+    /// Creates a new `Google_Protobuf_Duration` equal to the given number of
+    /// seconds and nanoseconds.
+    ///
+    /// - Parameter seconds: The number of seconds.
+    /// - Parameter nanos: The number of nanoseconds.
+    public init(seconds: Int64 = 0, nanos: Int32 = 0) {
+        self.init()
+        self.seconds = seconds
+        self.nanos = nanos
+    }
 }
 
 extension Google_Protobuf_Duration: _CustomJSONCodable {
-  mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    let s = try decoder.scanner.nextQuotedString()
-    (seconds, nanos) = try parseDuration(text: s)
-  }
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    if let formatted = formatDuration(seconds: seconds, nanos: nanos) {
-      return "\"\(formatted)\""
-    } else {
-      throw JSONEncodingError.durationRange
+    mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        let s = try decoder.scanner.nextQuotedString()
+        (seconds, nanos) = try parseDuration(text: s)
+    }
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        if let formatted = formatDuration(seconds: seconds, nanos: nanos) {
+            return "\"\(formatted)\""
+        } else {
+            throw JSONEncodingError.durationRange
+        }
     }
-  }
 }
 
 extension Google_Protobuf_Duration: ExpressibleByFloatLiteral {
-  public typealias FloatLiteralType = Double
-
-  /// Creates a new `Google_Protobuf_Duration` from a floating point literal
-  /// that is interpreted as a duration in seconds, rounded to the nearest
-  /// nanosecond.
-  public init(floatLiteral value: Double) {
-    let sd = trunc(value)
-    let nd = round((value - sd) * TimeInterval(nanosPerSecond))
-    let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd))
-    self.init(seconds: s, nanos: n)
-  }
+    public typealias FloatLiteralType = Double
+
+    /// Creates a new `Google_Protobuf_Duration` from a floating point literal
+    /// that is interpreted as a duration in seconds, rounded to the nearest
+    /// nanosecond.
+    public init(floatLiteral value: Double) {
+        let sd = trunc(value)
+        let nd = round((value - sd) * TimeInterval(nanosPerSecond))
+        let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd))
+        self.init(seconds: s, nanos: n)
+    }
 }
 
 extension Google_Protobuf_Duration {
-  /// Creates a new `Google_Protobuf_Duration` that is equal to the given
-  /// `TimeInterval` (measured in seconds), rounded to the nearest nanosecond.
-  ///
-  /// - Parameter timeInterval: The `TimeInterval`.
-  public init(timeInterval: TimeInterval) {
-    let sd = trunc(timeInterval)
-    let nd = round((timeInterval - sd) * TimeInterval(nanosPerSecond))
-    let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd))
-    self.init(seconds: s, nanos: n)
-  }
-
-  /// The `TimeInterval` (measured in seconds) equal to this duration.
-  public var timeInterval: TimeInterval {
-    return TimeInterval(self.seconds) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond)
-  }
+    /// Creates a new `Google_Protobuf_Duration` that is equal to the given
+    /// `TimeInterval` (measured in seconds), rounded to the nearest nanosecond.
+    ///
+    /// - Parameter timeInterval: The `TimeInterval`.
+    public init(timeInterval: TimeInterval) {
+        let sd = trunc(timeInterval)
+        let nd = round((timeInterval - sd) * TimeInterval(nanosPerSecond))
+        let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd))
+        self.init(seconds: s, nanos: n)
+    }
+
+    /// The `TimeInterval` (measured in seconds) equal to this duration.
+    public var timeInterval: TimeInterval {
+        TimeInterval(self.seconds) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond)
+    }
 }
 
 private func normalizeForDuration(
-  seconds: Int64,
-  nanos: Int32
+    seconds: Int64,
+    nanos: Int32
 ) -> (seconds: Int64, nanos: Int32) {
-  var s = seconds
-  var n = nanos
-
-  // If the magnitude of n exceeds a second then
-  // we need to factor it into s instead.
-  if n >= nanosPerSecond || n <= -nanosPerSecond {
-    s += Int64(n / nanosPerSecond)
-    n = n % nanosPerSecond
-  }
-
-  // The Duration spec says that when s != 0, s and
-  // n must have the same sign.
-  if s > 0 && n < 0 {
-    n += nanosPerSecond
-    s -= 1
-  } else if s < 0 && n > 0 {
-    n -= nanosPerSecond
-    s += 1
-  }
-
-  return (seconds: s, nanos: n)
+    var s = seconds
+    var n = nanos
+
+    // If the magnitude of n exceeds a second then
+    // we need to factor it into s instead.
+    if n >= nanosPerSecond || n <= -nanosPerSecond {
+        s += Int64(n / nanosPerSecond)
+        n = n % nanosPerSecond
+    }
+
+    // The Duration spec says that when s != 0, s and
+    // n must have the same sign.
+    if s > 0 && n < 0 {
+        n += nanosPerSecond
+        s -= 1
+    } else if s < 0 && n > 0 {
+        n -= nanosPerSecond
+        s += 1
+    }
+
+    return (seconds: s, nanos: n)
 }
 
 public prefix func - (
-  operand: Google_Protobuf_Duration
+    operand: Google_Protobuf_Duration
 ) -> Google_Protobuf_Duration {
-  let (s, n) = normalizeForDuration(seconds: -operand.seconds,
-                                    nanos: -operand.nanos)
-  return Google_Protobuf_Duration(seconds: s, nanos: n)
+    let (s, n) = normalizeForDuration(
+        seconds: -operand.seconds,
+        nanos: -operand.nanos
+    )
+    return Google_Protobuf_Duration(seconds: s, nanos: n)
 }
 
 public func + (
-  lhs: Google_Protobuf_Duration,
-  rhs: Google_Protobuf_Duration
+    lhs: Google_Protobuf_Duration,
+    rhs: Google_Protobuf_Duration
 ) -> Google_Protobuf_Duration {
-  let (s, n) = normalizeForDuration(seconds: lhs.seconds + rhs.seconds,
-                                    nanos: lhs.nanos + rhs.nanos)
-  return Google_Protobuf_Duration(seconds: s, nanos: n)
+    let (s, n) = normalizeForDuration(
+        seconds: lhs.seconds + rhs.seconds,
+        nanos: lhs.nanos + rhs.nanos
+    )
+    return Google_Protobuf_Duration(seconds: s, nanos: n)
 }
 
 public func - (
-  lhs: Google_Protobuf_Duration,
-  rhs: Google_Protobuf_Duration
+    lhs: Google_Protobuf_Duration,
+    rhs: Google_Protobuf_Duration
 ) -> Google_Protobuf_Duration {
-  let (s, n) = normalizeForDuration(seconds: lhs.seconds - rhs.seconds,
-                                    nanos: lhs.nanos - rhs.nanos)
-  return Google_Protobuf_Duration(seconds: s, nanos: n)
+    let (s, n) = normalizeForDuration(
+        seconds: lhs.seconds - rhs.seconds,
+        nanos: lhs.nanos - rhs.nanos
+    )
+    return Google_Protobuf_Duration(seconds: s, nanos: n)
 }
 
 public func - (
-  lhs: Google_Protobuf_Timestamp,
-  rhs: Google_Protobuf_Timestamp
+    lhs: Google_Protobuf_Timestamp,
+    rhs: Google_Protobuf_Timestamp
 ) -> Google_Protobuf_Duration {
-  let (s, n) = normalizeForDuration(seconds: lhs.seconds - rhs.seconds,
-                                    nanos: lhs.nanos - rhs.nanos)
-  return Google_Protobuf_Duration(seconds: s, nanos: n)
+    let (s, n) = normalizeForDuration(
+        seconds: lhs.seconds - rhs.seconds,
+        nanos: lhs.nanos - rhs.nanos
+    )
+    return Google_Protobuf_Duration(seconds: s, nanos: n)
 }

+ 280 - 281
Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift

@@ -13,356 +13,355 @@
 ///
 // -----------------------------------------------------------------------------
 
-
 // True if the string only contains printable (non-control)
 // ASCII characters.  Note: This follows the ASCII standard;
 // space is not a "printable" character.
 private func isPrintableASCII(_ s: String) -> Bool {
-  for u in s.utf8 {
-    if u <= 0x20 || u >= 0x7f {
-      return false
+    for u in s.utf8 {
+        if u <= 0x20 || u >= 0x7f {
+            return false
+        }
     }
-  }
-  return true
+    return true
 }
 
 private func ProtoToJSON(name: String) -> String? {
-  guard isPrintableASCII(name) else { return nil }
-  var jsonPath = String()
-  var chars = name.makeIterator()
-  while let c = chars.next() {
-    switch c {
-    case "_":
-      if let toupper = chars.next() {
-        switch toupper {
-        case "a"..."z":
-          jsonPath.append(String(toupper).uppercased())
+    guard isPrintableASCII(name) else { return nil }
+    var jsonPath = String()
+    var chars = name.makeIterator()
+    while let c = chars.next() {
+        switch c {
+        case "_":
+            if let toupper = chars.next() {
+                switch toupper {
+                case "a"..."z":
+                    jsonPath.append(String(toupper).uppercased())
+                default:
+                    return nil
+                }
+            } else {
+                return nil
+            }
+        case "A"..."Z":
+            return nil
+        case "a"..."z", "0"..."9", ".", "(", ")":
+            jsonPath.append(c)
         default:
-          return nil
+            // TODO: Change this to `return nil`
+            // once we know everything legal is handled
+            // above.
+            jsonPath.append(c)
         }
-      } else {
-        return nil
-      }
-    case "A"..."Z":
-      return nil
-    case "a"..."z","0"..."9",".","(",")":
-      jsonPath.append(c)
-    default:
-      // TODO: Change this to `return nil`
-      // once we know everything legal is handled
-      // above.
-      jsonPath.append(c)
     }
-  }
-  return jsonPath
+    return jsonPath
 }
 
 private func JSONToProto(name: String) -> String? {
-  guard isPrintableASCII(name) else { return nil }
-  var path = String()
-  for c in name {
-    switch c {
-    case "_":
-      return nil
-    case "A"..."Z":
-      path.append(Character("_"))
-      path.append(String(c).lowercased())
-    case "a"..."z","0"..."9",".","(",")":
-      path.append(c)
-    default:
-      // TODO: Change to `return nil` once
-      // we know everything legal is being
-      // handled above
-      path.append(c)
+    guard isPrintableASCII(name) else { return nil }
+    var path = String()
+    for c in name {
+        switch c {
+        case "_":
+            return nil
+        case "A"..."Z":
+            path.append(Character("_"))
+            path.append(String(c).lowercased())
+        case "a"..."z", "0"..."9", ".", "(", ")":
+            path.append(c)
+        default:
+            // TODO: Change to `return nil` once
+            // we know everything legal is being
+            // handled above
+            path.append(c)
+        }
     }
-  }
-  return path
+    return path
 }
 
 private func parseJSONFieldNames(names: String) -> [String]? {
-  // An empty field mask is the empty string (no paths).
-  guard !names.isEmpty else { return [] }
-  var fieldNameCount = 0
-  var fieldName = String()
-  var split = [String]()
-  for c in names {
-    switch c {
-    case ",":
-      if fieldNameCount == 0 {
+    // An empty field mask is the empty string (no paths).
+    guard !names.isEmpty else { return [] }
+    var fieldNameCount = 0
+    var fieldName = String()
+    var split = [String]()
+    for c in names {
+        switch c {
+        case ",":
+            if fieldNameCount == 0 {
+                return nil
+            }
+            if let pbName = JSONToProto(name: fieldName) {
+                split.append(pbName)
+            } else {
+                return nil
+            }
+            fieldName = String()
+            fieldNameCount = 0
+        default:
+            fieldName.append(c)
+            fieldNameCount += 1
+        }
+    }
+    if fieldNameCount == 0 {  // Last field name can't be empty
         return nil
-      }
-      if let pbName = JSONToProto(name: fieldName) {
+    }
+    if let pbName = JSONToProto(name: fieldName) {
         split.append(pbName)
-      } else {
+    } else {
         return nil
-      }
-      fieldName = String()
-      fieldNameCount = 0
-    default:
-      fieldName.append(c)
-      fieldNameCount += 1
     }
-  }
-  if fieldNameCount == 0 { // Last field name can't be empty
-    return nil
-  }
-  if let pbName = JSONToProto(name: fieldName) {
-    split.append(pbName)
-  } else {
-    return nil
-  }
-  return split
+    return split
 }
 
 extension Google_Protobuf_FieldMask {
-  /// Creates a new `Google_Protobuf_FieldMask` from the given array of paths.
-  ///
-  /// The paths should match the names used in the .proto file, which may be
-  /// different than the corresponding Swift property names.
-  ///
-  /// - Parameter protoPaths: The paths from which to create the field mask,
-  ///   defined using the .proto names for the fields.
-  public init(protoPaths: [String]) {
-    self.init()
-    paths = protoPaths
-  }
+    /// Creates a new `Google_Protobuf_FieldMask` from the given array of paths.
+    ///
+    /// The paths should match the names used in the .proto file, which may be
+    /// different than the corresponding Swift property names.
+    ///
+    /// - Parameter protoPaths: The paths from which to create the field mask,
+    ///   defined using the .proto names for the fields.
+    public init(protoPaths: [String]) {
+        self.init()
+        paths = protoPaths
+    }
 
-  /// Creates a new `Google_Protobuf_FieldMask` from the given paths.
-  ///
-  /// The paths should match the names used in the .proto file, which may be
-  /// different than the corresponding Swift property names.
-  ///
-  /// - Parameter protoPaths: The paths from which to create the field mask,
-  ///   defined using the .proto names for the fields.
-  public init(protoPaths: String...) {
-    self.init(protoPaths: protoPaths)
-  }
+    /// Creates a new `Google_Protobuf_FieldMask` from the given paths.
+    ///
+    /// The paths should match the names used in the .proto file, which may be
+    /// different than the corresponding Swift property names.
+    ///
+    /// - Parameter protoPaths: The paths from which to create the field mask,
+    ///   defined using the .proto names for the fields.
+    public init(protoPaths: String...) {
+        self.init(protoPaths: protoPaths)
+    }
 
-  /// Creates a new `Google_Protobuf_FieldMask` from the given paths.
-  ///
-  /// The paths should match the JSON names of the fields, which may be
-  /// different than the corresponding Swift property names.
-  ///
-  /// - Parameter jsonPaths: The paths from which to create the field mask,
-  ///   defined using the JSON names for the fields.
-  public init?(jsonPaths: String...) {
-    // TODO: This should fail if any of the conversions from JSON fails
-    self.init(protoPaths: jsonPaths.compactMap(JSONToProto))
-  }
+    /// Creates a new `Google_Protobuf_FieldMask` from the given paths.
+    ///
+    /// The paths should match the JSON names of the fields, which may be
+    /// different than the corresponding Swift property names.
+    ///
+    /// - Parameter jsonPaths: The paths from which to create the field mask,
+    ///   defined using the JSON names for the fields.
+    public init?(jsonPaths: String...) {
+        // TODO: This should fail if any of the conversions from JSON fails
+        self.init(protoPaths: jsonPaths.compactMap(JSONToProto))
+    }
 
-  // It would be nice if to have an initializer that accepted Swift property
-  // names, but translating between swift and protobuf/json property
-  // names is not entirely deterministic.
+    // It would be nice if to have an initializer that accepted Swift property
+    // names, but translating between swift and protobuf/json property
+    // names is not entirely deterministic.
 }
 
 extension Google_Protobuf_FieldMask: _CustomJSONCodable {
-  mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    let s = try decoder.scanner.nextQuotedString()
-    if let names = parseJSONFieldNames(names: s) {
-      paths = names
-    } else {
-      throw JSONDecodingError.malformedFieldMask
+    mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        let s = try decoder.scanner.nextQuotedString()
+        if let names = parseJSONFieldNames(names: s) {
+            paths = names
+        } else {
+            throw JSONDecodingError.malformedFieldMask
+        }
     }
-  }
 
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    // Note:  Proto requires alphanumeric field names, so there
-    // cannot be a ',' or '"' character to mess up this formatting.
-    var jsonPaths = [String]()
-    for p in paths {
-      if let jsonPath = ProtoToJSON(name: p) {
-        jsonPaths.append(jsonPath)
-      } else {
-        throw JSONEncodingError.fieldMaskConversion
-      }
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        // Note:  Proto requires alphanumeric field names, so there
+        // cannot be a ',' or '"' character to mess up this formatting.
+        var jsonPaths = [String]()
+        for p in paths {
+            if let jsonPath = ProtoToJSON(name: p) {
+                jsonPaths.append(jsonPath)
+            } else {
+                throw JSONEncodingError.fieldMaskConversion
+            }
+        }
+        return "\"" + jsonPaths.joined(separator: ",") + "\""
     }
-    return "\"" + jsonPaths.joined(separator: ",") + "\""
-  }
 }
 
 extension Google_Protobuf_FieldMask {
 
-  /// Initiates a field mask with all fields of the message type.
-  ///
-  /// - Parameter messageType: Message type to get all paths from.
-  public init<M: Message & _ProtoNameProviding>(
-    allFieldsOf messageType: M.Type
-  ) {
-    self = .with { mask in
-      mask.paths = M.allProtoNames
+    /// Initiates a field mask with all fields of the message type.
+    ///
+    /// - Parameter messageType: Message type to get all paths from.
+    public init<M: Message & _ProtoNameProviding>(
+        allFieldsOf messageType: M.Type
+    ) {
+        self = .with { mask in
+            mask.paths = M.allProtoNames
+        }
     }
-  }
 
-  /// Initiates a field mask from some particular field numbers of a message
-  ///
-  /// - Parameters:
-  ///   - messageType: Message type to get all paths from.
-  ///   - fieldNumbers: Field numbers of paths to be included.
-  /// - Returns: Field mask that include paths of corresponding field numbers.
-  /// - Throws: `FieldMaskError.invalidFieldNumber` if the field number
-  ///  is not on the message
-  public init<M: Message & _ProtoNameProviding>(
-    fieldNumbers: [Int],
-    of messageType: M.Type
-  ) throws {
-    var paths: [String] = []
-    for number in fieldNumbers {
-      guard let name = M.protoName(for: number) else {
-        throw FieldMaskError.invalidFieldNumber
-      }
-      paths.append(name)
-    }
-    self = .with { mask in
-      mask.paths = paths
+    /// Initiates a field mask from some particular field numbers of a message
+    ///
+    /// - Parameters:
+    ///   - messageType: Message type to get all paths from.
+    ///   - fieldNumbers: Field numbers of paths to be included.
+    /// - Returns: Field mask that include paths of corresponding field numbers.
+    /// - Throws: `FieldMaskError.invalidFieldNumber` if the field number
+    ///  is not on the message
+    public init<M: Message & _ProtoNameProviding>(
+        fieldNumbers: [Int],
+        of messageType: M.Type
+    ) throws {
+        var paths: [String] = []
+        for number in fieldNumbers {
+            guard let name = M.protoName(for: number) else {
+                throw FieldMaskError.invalidFieldNumber
+            }
+            paths.append(name)
+        }
+        self = .with { mask in
+            mask.paths = paths
+        }
     }
-  }
 }
 
 extension Google_Protobuf_FieldMask {
 
-  /// Adds a path to FieldMask after checking whether the given path is valid.
-  /// This method check-fails if the path is not a valid path for Message type.
-  ///
-  /// - Parameters:
-  ///   - path: Path to be added to FieldMask.
-  ///   - messageType: Message type to check validity.
-  public mutating func addPath<M: Message>(
-    _ path: String,
-    of messageType: M.Type
-  ) throws {
-    guard M.isPathValid(path) else {
-      throw FieldMaskError.invalidPath
+    /// Adds a path to FieldMask after checking whether the given path is valid.
+    /// This method check-fails if the path is not a valid path for Message type.
+    ///
+    /// - Parameters:
+    ///   - path: Path to be added to FieldMask.
+    ///   - messageType: Message type to check validity.
+    public mutating func addPath<M: Message>(
+        _ path: String,
+        of messageType: M.Type
+    ) throws {
+        guard M.isPathValid(path) else {
+            throw FieldMaskError.invalidPath
+        }
+        paths.append(path)
     }
-    paths.append(path)
-  }
 
-  /// Converts a FieldMask to the canonical form. It will:
-  ///   1. Remove paths that are covered by another path. For example,
-  ///      "foo.bar" is covered by "foo" and will be removed if "foo"
-  ///      is also in the FieldMask.
-  ///   2. Sort all paths in alphabetical order.
-  public var canonical: Google_Protobuf_FieldMask {
-    var mask = Google_Protobuf_FieldMask()
-    let sortedPaths = self.paths.sorted()
-    for path in sortedPaths {
-      if let lastPath = mask.paths.last {
-        if path != lastPath, !path.hasPrefix("\(lastPath).") {
-          mask.paths.append(path)
+    /// Converts a FieldMask to the canonical form. It will:
+    ///   1. Remove paths that are covered by another path. For example,
+    ///      "foo.bar" is covered by "foo" and will be removed if "foo"
+    ///      is also in the FieldMask.
+    ///   2. Sort all paths in alphabetical order.
+    public var canonical: Google_Protobuf_FieldMask {
+        var mask = Google_Protobuf_FieldMask()
+        let sortedPaths = self.paths.sorted()
+        for path in sortedPaths {
+            if let lastPath = mask.paths.last {
+                if path != lastPath, !path.hasPrefix("\(lastPath).") {
+                    mask.paths.append(path)
+                }
+            } else {
+                mask.paths.append(path)
+            }
         }
-      } else {
-        mask.paths.append(path)
-      }
+        return mask
     }
-    return mask
-  }
 
-  /// Creates an union of two FieldMasks.
-  ///
-  /// - Parameter mask: FieldMask to union with.
-  /// - Returns: FieldMask with union of two path sets.
-  public func union(
-    _ mask: Google_Protobuf_FieldMask
-  ) -> Google_Protobuf_FieldMask {
-    var buffer: Set<String> = .init()
-    var paths: [String] = []
-    let allPaths = self.paths + mask.paths
-    for path in allPaths where !buffer.contains(path) {
-      buffer.insert(path)
-      paths.append(path)
-    }
-    return .with { mask in
-      mask.paths = paths
+    /// Creates an union of two FieldMasks.
+    ///
+    /// - Parameter mask: FieldMask to union with.
+    /// - Returns: FieldMask with union of two path sets.
+    public func union(
+        _ mask: Google_Protobuf_FieldMask
+    ) -> Google_Protobuf_FieldMask {
+        var buffer: Set<String> = .init()
+        var paths: [String] = []
+        let allPaths = self.paths + mask.paths
+        for path in allPaths where !buffer.contains(path) {
+            buffer.insert(path)
+            paths.append(path)
+        }
+        return .with { mask in
+            mask.paths = paths
+        }
     }
-  }
 
-  /// Creates an intersection of two FieldMasks.
-  ///
-  /// - Parameter mask: FieldMask to intersect with.
-  /// - Returns: FieldMask with intersection of two path sets.
-  public func intersect(
-    _ mask: Google_Protobuf_FieldMask
-  ) -> Google_Protobuf_FieldMask {
-    let set = Set<String>(mask.paths)
-    var paths: [String] = []
-    var buffer = Set<String>()
-    for path in self.paths where set.contains(path) && !buffer.contains(path) {
-      buffer.insert(path)
-      paths.append(path)
-    }
-    return .with { mask in
-      mask.paths = paths
+    /// Creates an intersection of two FieldMasks.
+    ///
+    /// - Parameter mask: FieldMask to intersect with.
+    /// - Returns: FieldMask with intersection of two path sets.
+    public func intersect(
+        _ mask: Google_Protobuf_FieldMask
+    ) -> Google_Protobuf_FieldMask {
+        let set = Set<String>(mask.paths)
+        var paths: [String] = []
+        var buffer = Set<String>()
+        for path in self.paths where set.contains(path) && !buffer.contains(path) {
+            buffer.insert(path)
+            paths.append(path)
+        }
+        return .with { mask in
+            mask.paths = paths
+        }
     }
-  }
 
-  /// Creates a FieldMasks with paths of the original FieldMask
-  /// that does not included in mask.
-  ///
-  /// - Parameter mask: FieldMask with paths should be substracted.
-  /// - Returns: FieldMask with all paths does not included in mask.
-  public func subtract(
-    _ mask: Google_Protobuf_FieldMask
-  ) -> Google_Protobuf_FieldMask {
-    let set = Set<String>(mask.paths)
-    var paths: [String] = []
-    var buffer = Set<String>()
-    for path in self.paths where !set.contains(path) && !buffer.contains(path) {
-      buffer.insert(path)
-      paths.append(path)
-    }
-    return .with { mask in
-      mask.paths = paths
+    /// Creates a FieldMasks with paths of the original FieldMask
+    /// that does not included in mask.
+    ///
+    /// - Parameter mask: FieldMask with paths should be substracted.
+    /// - Returns: FieldMask with all paths does not included in mask.
+    public func subtract(
+        _ mask: Google_Protobuf_FieldMask
+    ) -> Google_Protobuf_FieldMask {
+        let set = Set<String>(mask.paths)
+        var paths: [String] = []
+        var buffer = Set<String>()
+        for path in self.paths where !set.contains(path) && !buffer.contains(path) {
+            buffer.insert(path)
+            paths.append(path)
+        }
+        return .with { mask in
+            mask.paths = paths
+        }
     }
-  }
 
-  /// Returns true if path is covered by the given FieldMask. Note that path
-  /// "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc.
-  /// Also note that parent paths are not covered by explicit child path, i.e.
-  /// "foo.bar" does NOT cover "foo", even if "bar" is the only child.
-  ///
-  /// - Parameter path: Path to be checked.
-  /// - Returns: Boolean determines is path covered.
-  public func contains(_ path: String) -> Bool {
-    for fieldMaskPath in paths {
-      if path.hasPrefix("\(fieldMaskPath).") || fieldMaskPath == path {
-        return true
-      }
+    /// Returns true if path is covered by the given FieldMask. Note that path
+    /// "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc.
+    /// Also note that parent paths are not covered by explicit child path, i.e.
+    /// "foo.bar" does NOT cover "foo", even if "bar" is the only child.
+    ///
+    /// - Parameter path: Path to be checked.
+    /// - Returns: Boolean determines is path covered.
+    public func contains(_ path: String) -> Bool {
+        for fieldMaskPath in paths {
+            if path.hasPrefix("\(fieldMaskPath).") || fieldMaskPath == path {
+                return true
+            }
+        }
+        return false
     }
-    return false
-  }
 }
 
 extension Google_Protobuf_FieldMask {
 
-  /// Checks whether the given FieldMask is valid for type M.
-  ///
-  /// - Parameter messageType: Message type to paths check with.
-  /// - Returns: Boolean determines FieldMask is valid.
-  public func isValid<M: Message & _ProtoNameProviding>(
-    for messageType: M.Type
-  ) -> Bool {
-    var message = M()
-    return paths.allSatisfy { path in
-      message.isPathValid(path)
+    /// Checks whether the given FieldMask is valid for type M.
+    ///
+    /// - Parameter messageType: Message type to paths check with.
+    /// - Returns: Boolean determines FieldMask is valid.
+    public func isValid<M: Message & _ProtoNameProviding>(
+        for messageType: M.Type
+    ) -> Bool {
+        var message = M()
+        return paths.allSatisfy { path in
+            message.isPathValid(path)
+        }
     }
-  }
 }
 
 /// Describes errors could happen during FieldMask utilities.
 public enum FieldMaskError: Error {
 
-  /// Describes a path is invalid for a Message type.
-  case invalidPath
+    /// Describes a path is invalid for a Message type.
+    case invalidPath
 
-  /// Describes a fieldNumber is invalid for a Message type.
-  case invalidFieldNumber
+    /// Describes a fieldNumber is invalid for a Message type.
+    case invalidFieldNumber
 }
 
-private extension Message where Self: _ProtoNameProviding {
-  static func protoName(for number: Int) -> String? {
-    Self._protobuf_nameMap.names(for: number)?.proto.description
-  }
+extension Message where Self: _ProtoNameProviding {
+    fileprivate static func protoName(for number: Int) -> String? {
+        Self._protobuf_nameMap.names(for: number)?.proto.description
+    }
 
-  static var allProtoNames: [String] {
-    Self._protobuf_nameMap.names.map(\.description)
-  }
+    fileprivate static var allProtoNames: [String] {
+        Self._protobuf_nameMap.names.map(\.description)
+    }
 }

+ 57 - 57
Sources/SwiftProtobuf/Google_Protobuf_ListValue+Extensions.swift

@@ -14,72 +14,72 @@
 // -----------------------------------------------------------------------------
 
 extension Google_Protobuf_ListValue: ExpressibleByArrayLiteral {
-  // TODO: Give this a direct array interface by proxying the interesting
-  // bits down to values
-  public typealias Element = Google_Protobuf_Value
+    // TODO: Give this a direct array interface by proxying the interesting
+    // bits down to values
+    public typealias Element = Google_Protobuf_Value
 
-  /// Creates a new `Google_Protobuf_ListValue` from an array literal containing
-  /// `Google_Protobuf_Value` elements.
-  public init(arrayLiteral elements: Element...) {
-    self.init(values: elements)
-  }
+    /// Creates a new `Google_Protobuf_ListValue` from an array literal containing
+    /// `Google_Protobuf_Value` elements.
+    public init(arrayLiteral elements: Element...) {
+        self.init(values: elements)
+    }
 }
 
 extension Google_Protobuf_ListValue: _CustomJSONCodable {
-  internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    var jsonEncoder = JSONEncoder()
-    jsonEncoder.append(text: "[")
-    var separator: StaticString = ""
-    for v in values {
-      jsonEncoder.append(staticText: separator)
-      try v.serializeJSONValue(to: &jsonEncoder, options: options)
-      separator = ","
+    internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        var jsonEncoder = JSONEncoder()
+        jsonEncoder.append(text: "[")
+        var separator: StaticString = ""
+        for v in values {
+            jsonEncoder.append(staticText: separator)
+            try v.serializeJSONValue(to: &jsonEncoder, options: options)
+            separator = ","
+        }
+        jsonEncoder.append(text: "]")
+        return jsonEncoder.stringResult
     }
-    jsonEncoder.append(text: "]")
-    return jsonEncoder.stringResult
-  }
 
-  internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    if decoder.scanner.skipOptionalNull() {
-      return
-    }
-    try decoder.scanner.skipRequiredArrayStart()
-    // Since we override the JSON decoding, we can't rely
-    // on the default recursion depth tracking.
-    try decoder.scanner.incrementRecursionDepth()
-    if decoder.scanner.skipOptionalArrayEnd() {
-      decoder.scanner.decrementRecursionDepth()
-      return
-    }
-    while true {
-      var v = Google_Protobuf_Value()
-      try v.decodeJSON(from: &decoder)
-      values.append(v)
-      if decoder.scanner.skipOptionalArrayEnd() {
-        decoder.scanner.decrementRecursionDepth()
-        return
-      }
-      try decoder.scanner.skipRequiredComma()
+    internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        if decoder.scanner.skipOptionalNull() {
+            return
+        }
+        try decoder.scanner.skipRequiredArrayStart()
+        // Since we override the JSON decoding, we can't rely
+        // on the default recursion depth tracking.
+        try decoder.scanner.incrementRecursionDepth()
+        if decoder.scanner.skipOptionalArrayEnd() {
+            decoder.scanner.decrementRecursionDepth()
+            return
+        }
+        while true {
+            var v = Google_Protobuf_Value()
+            try v.decodeJSON(from: &decoder)
+            values.append(v)
+            if decoder.scanner.skipOptionalArrayEnd() {
+                decoder.scanner.decrementRecursionDepth()
+                return
+            }
+            try decoder.scanner.skipRequiredComma()
+        }
     }
-  }
 }
 
 extension Google_Protobuf_ListValue {
-  /// Creates a new `Google_Protobuf_ListValue` from the given array of
-  /// `Google_Protobuf_Value` elements.
-  ///
-  /// - Parameter values: The list of `Google_Protobuf_Value` messages from
-  ///   which to create the `Google_Protobuf_ListValue`.
-  public init(values: [Google_Protobuf_Value]) {
-    self.init()
-    self.values = values
-  }
+    /// Creates a new `Google_Protobuf_ListValue` from the given array of
+    /// `Google_Protobuf_Value` elements.
+    ///
+    /// - Parameter values: The list of `Google_Protobuf_Value` messages from
+    ///   which to create the `Google_Protobuf_ListValue`.
+    public init(values: [Google_Protobuf_Value]) {
+        self.init()
+        self.values = values
+    }
 
-  /// Accesses the `Google_Protobuf_Value` at the specified position.
-  ///
-  /// - Parameter index: The position of the element to access.
-  public subscript(index: Int) -> Google_Protobuf_Value {
-    get {return values[index]}
-    set(newValue) {values[index] = newValue}
-  }
+    /// Accesses the `Google_Protobuf_Value` at the specified position.
+    ///
+    /// - Parameter index: The position of the element to access.
+    public subscript(index: Int) -> Google_Protobuf_Value {
+        get { values[index] }
+        set(newValue) { values[index] = newValue }
+    }
 }

+ 10 - 10
Sources/SwiftProtobuf/Google_Protobuf_NullValue+Extensions.swift

@@ -14,15 +14,15 @@
 // -----------------------------------------------------------------------------
 
 extension Google_Protobuf_NullValue: _CustomJSONCodable {
-  internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    return "null"
-  }
-  internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    if decoder.scanner.skipOptionalNull() {
-      return
+    internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        "null"
+    }
+    internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        if decoder.scanner.skipOptionalNull() {
+            return
+        }
+    }
+    static func decodedFromJSONNull() -> Google_Protobuf_NullValue? {
+        .nullValue
     }
-  }
-  static func decodedFromJSONNull() -> Google_Protobuf_NullValue? {
-    return .nullValue
-  }
 }

+ 56 - 56
Sources/SwiftProtobuf/Google_Protobuf_Struct+Extensions.swift

@@ -14,72 +14,72 @@
 // -----------------------------------------------------------------------------
 
 extension Google_Protobuf_Struct: ExpressibleByDictionaryLiteral {
-  public typealias Key = String
-  public typealias Value = Google_Protobuf_Value
+    public typealias Key = String
+    public typealias Value = Google_Protobuf_Value
 
-  /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to
-  /// values of type `Google_Protobuf_Value`.
-  public init(dictionaryLiteral: (String, Google_Protobuf_Value)...) {
-    self.init()
-    for (k,v) in dictionaryLiteral {
-      fields[k] = v
+    /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to
+    /// values of type `Google_Protobuf_Value`.
+    public init(dictionaryLiteral: (String, Google_Protobuf_Value)...) {
+        self.init()
+        for (k, v) in dictionaryLiteral {
+            fields[k] = v
+        }
     }
-  }
 }
 
 extension Google_Protobuf_Struct: _CustomJSONCodable {
-  internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    var jsonEncoder = JSONEncoder()
-    jsonEncoder.startObject()
-    var mapVisitor = JSONMapEncodingVisitor(encoder: jsonEncoder, options: options)
-    for (k,v) in fields {
-      try mapVisitor.visitSingularStringField(value: k, fieldNumber: 1)
-      try mapVisitor.visitSingularMessageField(value: v, fieldNumber: 2)
+    internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        var jsonEncoder = JSONEncoder()
+        jsonEncoder.startObject()
+        var mapVisitor = JSONMapEncodingVisitor(encoder: jsonEncoder, options: options)
+        for (k, v) in fields {
+            try mapVisitor.visitSingularStringField(value: k, fieldNumber: 1)
+            try mapVisitor.visitSingularMessageField(value: v, fieldNumber: 2)
+        }
+        mapVisitor.encoder.endObject()
+        return mapVisitor.encoder.stringResult
     }
-    mapVisitor.encoder.endObject()
-    return mapVisitor.encoder.stringResult
-  }
 
-  internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    try decoder.scanner.skipRequiredObjectStart()
-    if decoder.scanner.skipOptionalObjectEnd() {
-      return
+    internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        try decoder.scanner.skipRequiredObjectStart()
+        if decoder.scanner.skipOptionalObjectEnd() {
+            return
+        }
+        while true {
+            let key = try decoder.scanner.nextQuotedString()
+            try decoder.scanner.skipRequiredColon()
+            var value = Google_Protobuf_Value()
+            try value.decodeJSON(from: &decoder)
+            fields[key] = value
+            if decoder.scanner.skipOptionalObjectEnd() {
+                return
+            }
+            try decoder.scanner.skipRequiredComma()
+        }
     }
-    while true {
-      let key = try decoder.scanner.nextQuotedString()
-      try decoder.scanner.skipRequiredColon()
-      var value = Google_Protobuf_Value()
-      try value.decodeJSON(from: &decoder)
-      fields[key] = value
-      if decoder.scanner.skipOptionalObjectEnd() {
-        return
-      }
-      try decoder.scanner.skipRequiredComma()
-    }
-  }
 }
 
 extension Google_Protobuf_Struct {
-  /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to
-  /// values of type `Google_Protobuf_Value`.
-  ///
-  /// - Parameter fields: The dictionary from field names to
-  ///   `Google_Protobuf_Value` messages that should be used to create the
-  ///   `Struct`.
-  public init(fields: [String: Google_Protobuf_Value]) {
-    self.init()
-    self.fields = fields
-  }
+    /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to
+    /// values of type `Google_Protobuf_Value`.
+    ///
+    /// - Parameter fields: The dictionary from field names to
+    ///   `Google_Protobuf_Value` messages that should be used to create the
+    ///   `Struct`.
+    public init(fields: [String: Google_Protobuf_Value]) {
+        self.init()
+        self.fields = fields
+    }
 
-  /// Accesses the `Google_Protobuf_Value` with the given key for reading and
-  /// writing.
-  ///
-  /// This key-based subscript returns the `Value` for the given key if the key
-  /// is found in the `Struct`, or nil if the key is not found. If you assign
-  /// nil as the `Value` for the given key, the `Struct` removes that key and
-  /// its associated `Value`.
-  public subscript(key: String) -> Google_Protobuf_Value? {
-    get {return fields[key]}
-    set(newValue) {fields[key] = newValue}
-  }
+    /// Accesses the `Google_Protobuf_Value` with the given key for reading and
+    /// writing.
+    ///
+    /// This key-based subscript returns the `Value` for the given key if the key
+    /// is found in the `Struct`, or nil if the key is not found. If you assign
+    /// nil as the `Value` for the given key, the `Struct` removes that key and
+    /// its associated `Value`.
+    public subscript(key: String) -> Google_Protobuf_Value? {
+        get { fields[key] }
+        set(newValue) { fields[key] = newValue }
+    }
 }

+ 274 - 265
Sources/SwiftProtobuf/Google_Protobuf_Timestamp+Extensions.swift

@@ -15,317 +15,326 @@
 
 import Foundation
 
-private let minTimestampSeconds: Int64 = -62135596800  // 0001-01-01T00:00:00Z
-private let maxTimestampSeconds: Int64 = 253402300799  // 9999-12-31T23:59:59Z
+private let minTimestampSeconds: Int64 = -62_135_596_800  // 0001-01-01T00:00:00Z
+private let maxTimestampSeconds: Int64 = 253_402_300_799  // 9999-12-31T23:59:59Z
 
 // TODO: Add convenience methods to interoperate with standard
 // date/time classes:  an initializer that accepts Unix timestamp as
 // Int or Double, an easy way to convert to/from Foundation's
 // NSDateTime (on Apple platforms only?), others?
 
-
 // Parse an RFC3339 timestamp into a pair of seconds-since-1970 and nanos.
 private func parseTimestamp(s: String) throws -> (Int64, Int32) {
-  // Convert to an array of integer character values
-  let value = s.utf8.map{Int($0)}
-  if value.count < 20 {
-    throw JSONDecodingError.malformedTimestamp
-  }
-  // Since the format is fixed-layout, we can just decode
-  // directly as follows.
-  let zero = Int(48)
-  let nine = Int(57)
-  let dash = Int(45)
-  let colon = Int(58)
-  let plus = Int(43)
-  let letterT = Int(84)
-  let letterZ = Int(90)
-  let period = Int(46)
-
-  func fromAscii2(_ digit0: Int, _ digit1: Int) throws -> Int {
-    if digit0 < zero || digit0 > nine || digit1 < zero || digit1 > nine {
-      throw JSONDecodingError.malformedTimestamp
+    // Convert to an array of integer character values
+    let value = s.utf8.map { Int($0) }
+    if value.count < 20 {
+        throw JSONDecodingError.malformedTimestamp
+    }
+    // Since the format is fixed-layout, we can just decode
+    // directly as follows.
+    let zero = Int(48)
+    let nine = Int(57)
+    let dash = Int(45)
+    let colon = Int(58)
+    let plus = Int(43)
+    let letterT = Int(84)
+    let letterZ = Int(90)
+    let period = Int(46)
+
+    func fromAscii2(_ digit0: Int, _ digit1: Int) throws -> Int {
+        if digit0 < zero || digit0 > nine || digit1 < zero || digit1 > nine {
+            throw JSONDecodingError.malformedTimestamp
+        }
+        return digit0 * 10 + digit1 - 528
+    }
+
+    func fromAscii4(
+        _ digit0: Int,
+        _ digit1: Int,
+        _ digit2: Int,
+        _ digit3: Int
+    ) throws -> Int {
+        if digit0 < zero || digit0 > nine
+            || digit1 < zero || digit1 > nine
+            || digit2 < zero || digit2 > nine
+            || digit3 < zero || digit3 > nine
+        {
+            throw JSONDecodingError.malformedTimestamp
+        }
+        return digit0 * 1000 + digit1 * 100 + digit2 * 10 + digit3 - 53328
+    }
+
+    // Year: 4 digits followed by '-'
+    let year = try fromAscii4(value[0], value[1], value[2], value[3])
+    if value[4] != dash || year < Int(1) || year > Int(9999) {
+        throw JSONDecodingError.malformedTimestamp
     }
-    return digit0 * 10 + digit1 - 528
-  }
-
-  func fromAscii4(
-    _ digit0: Int,
-    _ digit1: Int,
-    _ digit2: Int,
-    _ digit3: Int
-  ) throws -> Int {
-    if (digit0 < zero || digit0 > nine
-      || digit1 < zero || digit1 > nine
-      || digit2 < zero || digit2 > nine
-      || digit3 < zero || digit3 > nine) {
-      throw JSONDecodingError.malformedTimestamp
+
+    // Month: 2 digits followed by '-'
+    let month = try fromAscii2(value[5], value[6])
+    if value[7] != dash || month < Int(1) || month > Int(12) {
+        throw JSONDecodingError.malformedTimestamp
     }
-    return digit0 * 1000 + digit1 * 100 + digit2 * 10 + digit3 - 53328
-  }
-
-  // Year: 4 digits followed by '-'
-  let year = try fromAscii4(value[0], value[1], value[2], value[3])
-  if value[4] != dash || year < Int(1) || year > Int(9999) {
-    throw JSONDecodingError.malformedTimestamp
-  }
-
-  // Month: 2 digits followed by '-'
-  let month = try fromAscii2(value[5], value[6])
-  if value[7] != dash || month < Int(1) || month > Int(12) {
-    throw JSONDecodingError.malformedTimestamp
-  }
-
-  // Day: 2 digits followed by 'T'
-  let mday = try fromAscii2(value[8], value[9])
-  if value[10] != letterT || mday < Int(1) || mday > Int(31) {
-    throw JSONDecodingError.malformedTimestamp
-  }
-
-  // Hour: 2 digits followed by ':'
-  let hour = try fromAscii2(value[11], value[12])
-  if value[13] != colon || hour > Int(23) {
-    throw JSONDecodingError.malformedTimestamp
-  }
-
-  // Minute: 2 digits followed by ':'
-  let minute = try fromAscii2(value[14], value[15])
-  if value[16] != colon || minute > Int(59) {
-    throw JSONDecodingError.malformedTimestamp
-  }
-
-  // Second: 2 digits (following char is checked below)
-  let second = try fromAscii2(value[17], value[18])
-  if second > Int(61) {
-    throw JSONDecodingError.malformedTimestamp
-  }
-
-  // timegm() is almost entirely useless.  It's nonexistent on
-  // some platforms, broken on others.  Everything else I've tried
-  // is even worse.  Hence the code below.
-  // (If you have a better way to do this, try it and see if it
-  // passes the test suite on both Linux and OS X.)
-
-  // Day of year
-  let mdayStart: [Int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
-  var yday = Int64(mdayStart[month - 1])
-  let isleap = (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0))
-  if isleap && (month > 2) {
-    yday += 1
-  }
-  yday += Int64(mday - 1)
-
-  // Days since start of epoch (including leap days)
-  var daysSinceEpoch = yday
-  daysSinceEpoch += Int64(365 * year) - Int64(719527)
-  daysSinceEpoch += Int64((year - 1) / 4)
-  daysSinceEpoch -= Int64((year - 1) / 100)
-  daysSinceEpoch += Int64((year - 1) / 400)
-
-  // Second within day
-  var daySec = Int64(hour)
-  daySec *= 60
-  daySec += Int64(minute)
-  daySec *= 60
-  daySec += Int64(second)
-
-  // Seconds since start of epoch
-  let t = daysSinceEpoch * Int64(86400) + daySec
-
-  // After seconds, comes various optional bits
-  var pos = 19
-
-  var nanos: Int32 = 0
-  if value[pos] == period { // "." begins fractional seconds
-    pos += 1
-    var digitValue = 100000000
-    while pos < value.count && value[pos] >= zero && value[pos] <= nine {
-      nanos += Int32(digitValue * (value[pos] - zero))
-      digitValue /= 10
-      pos += 1
+
+    // Day: 2 digits followed by 'T'
+    let mday = try fromAscii2(value[8], value[9])
+    if value[10] != letterT || mday < Int(1) || mday > Int(31) {
+        throw JSONDecodingError.malformedTimestamp
     }
-  }
-
-  var seconds: Int64 = 0
-  // "Z" or "+" or "-" starts Timezone offset
-  if pos >= value.count {
-    throw JSONDecodingError.malformedTimestamp
-  } else if value[pos] == plus || value[pos] == dash {
-    if pos + 6 > value.count {
-      throw JSONDecodingError.malformedTimestamp
+
+    // Hour: 2 digits followed by ':'
+    let hour = try fromAscii2(value[11], value[12])
+    if value[13] != colon || hour > Int(23) {
+        throw JSONDecodingError.malformedTimestamp
     }
-    let hourOffset = try fromAscii2(value[pos + 1], value[pos + 2])
-    let minuteOffset = try fromAscii2(value[pos + 4], value[pos + 5])
-    if hourOffset > Int(13) || minuteOffset > Int(59) || value[pos + 3] != colon {
-      throw JSONDecodingError.malformedTimestamp
+
+    // Minute: 2 digits followed by ':'
+    let minute = try fromAscii2(value[14], value[15])
+    if value[16] != colon || minute > Int(59) {
+        throw JSONDecodingError.malformedTimestamp
     }
-    var adjusted: Int64 = t
-    if value[pos] == plus {
-      adjusted -= Int64(hourOffset) * Int64(3600)
-      adjusted -= Int64(minuteOffset) * Int64(60)
+
+    // Second: 2 digits (following char is checked below)
+    let second = try fromAscii2(value[17], value[18])
+    if second > Int(61) {
+        throw JSONDecodingError.malformedTimestamp
+    }
+
+    // timegm() is almost entirely useless.  It's nonexistent on
+    // some platforms, broken on others.  Everything else I've tried
+    // is even worse.  Hence the code below.
+    // (If you have a better way to do this, try it and see if it
+    // passes the test suite on both Linux and OS X.)
+
+    // Day of year
+    let mdayStart: [Int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
+    var yday = Int64(mdayStart[month - 1])
+    let isleap = (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0))
+    if isleap && (month > 2) {
+        yday += 1
+    }
+    yday += Int64(mday - 1)
+
+    // Days since start of epoch (including leap days)
+    var daysSinceEpoch = yday
+    daysSinceEpoch += Int64(365 * year) - Int64(719527)
+    daysSinceEpoch += Int64((year - 1) / 4)
+    daysSinceEpoch -= Int64((year - 1) / 100)
+    daysSinceEpoch += Int64((year - 1) / 400)
+
+    // Second within day
+    var daySec = Int64(hour)
+    daySec *= 60
+    daySec += Int64(minute)
+    daySec *= 60
+    daySec += Int64(second)
+
+    // Seconds since start of epoch
+    let t = daysSinceEpoch * Int64(86400) + daySec
+
+    // After seconds, comes various optional bits
+    var pos = 19
+
+    var nanos: Int32 = 0
+    if value[pos] == period {  // "." begins fractional seconds
+        pos += 1
+        var digitValue = 100_000_000
+        while pos < value.count && value[pos] >= zero && value[pos] <= nine {
+            nanos += Int32(digitValue * (value[pos] - zero))
+            digitValue /= 10
+            pos += 1
+        }
+    }
+
+    var seconds: Int64 = 0
+    // "Z" or "+" or "-" starts Timezone offset
+    if pos >= value.count {
+        throw JSONDecodingError.malformedTimestamp
+    } else if value[pos] == plus || value[pos] == dash {
+        if pos + 6 > value.count {
+            throw JSONDecodingError.malformedTimestamp
+        }
+        let hourOffset = try fromAscii2(value[pos + 1], value[pos + 2])
+        let minuteOffset = try fromAscii2(value[pos + 4], value[pos + 5])
+        if hourOffset > Int(13) || minuteOffset > Int(59) || value[pos + 3] != colon {
+            throw JSONDecodingError.malformedTimestamp
+        }
+        var adjusted: Int64 = t
+        if value[pos] == plus {
+            adjusted -= Int64(hourOffset) * Int64(3600)
+            adjusted -= Int64(minuteOffset) * Int64(60)
+        } else {
+            adjusted += Int64(hourOffset) * Int64(3600)
+            adjusted += Int64(minuteOffset) * Int64(60)
+        }
+        seconds = adjusted
+        pos += 6
+    } else if value[pos] == letterZ {  // "Z" indicator for UTC
+        seconds = t
+        pos += 1
     } else {
-      adjusted += Int64(hourOffset) * Int64(3600)
-      adjusted += Int64(minuteOffset) * Int64(60)
+        throw JSONDecodingError.malformedTimestamp
+    }
+    if pos != value.count {
+        throw JSONDecodingError.malformedTimestamp
     }
-    seconds = adjusted
-    pos += 6
-  } else if value[pos] == letterZ { // "Z" indicator for UTC
-    seconds = t
-    pos += 1
-  } else {
-    throw JSONDecodingError.malformedTimestamp
-  }
-  if pos != value.count {
-    throw JSONDecodingError.malformedTimestamp
-  }
-  guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else {
-    throw JSONDecodingError.malformedTimestamp
-  }
-  return (seconds, nanos)
+    guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else {
+        throw JSONDecodingError.malformedTimestamp
+    }
+    return (seconds, nanos)
 }
 
 private func formatTimestamp(seconds: Int64, nanos: Int32) -> String? {
-  let (seconds, nanos) = normalizeForTimestamp(seconds: seconds, nanos: nanos)
-  guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else {
-    return nil
-  }
+    let (seconds, nanos) = normalizeForTimestamp(seconds: seconds, nanos: nanos)
+    guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else {
+        return nil
+    }
 
-  let (hh, mm, ss) = timeOfDayFromSecondsSince1970(seconds: seconds)
-  let (YY, MM, DD) = gregorianDateFromSecondsSince1970(seconds: seconds)
+    let (hh, mm, ss) = timeOfDayFromSecondsSince1970(seconds: seconds)
+    let (YY, MM, DD) = gregorianDateFromSecondsSince1970(seconds: seconds)
 
-  let dateString = "\(fourDigit(YY))-\(twoDigit(MM))-\(twoDigit(DD))"
-  let timeString = "\(twoDigit(hh)):\(twoDigit(mm)):\(twoDigit(ss))"
-  let nanosString = nanosToString(nanos: nanos) // Includes leading '.' if needed
+    let dateString = "\(fourDigit(YY))-\(twoDigit(MM))-\(twoDigit(DD))"
+    let timeString = "\(twoDigit(hh)):\(twoDigit(mm)):\(twoDigit(ss))"
+    let nanosString = nanosToString(nanos: nanos)  // Includes leading '.' if needed
 
-  return "\(dateString)T\(timeString)\(nanosString)Z"
+    return "\(dateString)T\(timeString)\(nanosString)Z"
 }
 
 extension Google_Protobuf_Timestamp {
-  /// Creates a new `Google_Protobuf_Timestamp` equal to the given number of
-  /// seconds and nanoseconds.
-  ///
-  /// - Parameter seconds: The number of seconds.
-  /// - Parameter nanos: The number of nanoseconds.
-  public init(seconds: Int64 = 0, nanos: Int32 = 0) {
-    self.init()
-    self.seconds = seconds
-    self.nanos = nanos
-  }
+    /// Creates a new `Google_Protobuf_Timestamp` equal to the given number of
+    /// seconds and nanoseconds.
+    ///
+    /// - Parameter seconds: The number of seconds.
+    /// - Parameter nanos: The number of nanoseconds.
+    public init(seconds: Int64 = 0, nanos: Int32 = 0) {
+        self.init()
+        self.seconds = seconds
+        self.nanos = nanos
+    }
 }
 
 extension Google_Protobuf_Timestamp: _CustomJSONCodable {
-  mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    let s = try decoder.scanner.nextQuotedString()
-    (seconds, nanos) = try parseTimestamp(s: s)
-  }
-
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    if let formatted = formatTimestamp(seconds: seconds, nanos: nanos) {
-      return "\"\(formatted)\""
-    } else {
-      throw JSONEncodingError.timestampRange
+    mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        let s = try decoder.scanner.nextQuotedString()
+        (seconds, nanos) = try parseTimestamp(s: s)
+    }
+
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        if let formatted = formatTimestamp(seconds: seconds, nanos: nanos) {
+            return "\"\(formatted)\""
+        } else {
+            throw JSONEncodingError.timestampRange
+        }
     }
-  }
 }
 
 extension Google_Protobuf_Timestamp {
-  /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00
-  /// UTC on 1 January 1970 by a given number of seconds.
-  ///
-  /// - Parameter timeIntervalSince1970: The `TimeInterval`, interpreted as
-  ///   seconds relative to 00:00:00 UTC on 1 January 1970.
-  public init(timeIntervalSince1970: TimeInterval) {
-    let sd = floor(timeIntervalSince1970)
-    let nd = round((timeIntervalSince1970 - sd) * TimeInterval(nanosPerSecond))
-    let (s, n) = normalizeForTimestamp(seconds: Int64(sd), nanos: Int32(nd))
-    self.init(seconds: s, nanos: n)
-  }
-
-  /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00
-  /// UTC on 1 January 2001 by a given number of seconds.
-  ///
-  /// - Parameter timeIntervalSinceReferenceDate: The `TimeInterval`,
-  ///   interpreted as seconds relative to 00:00:00 UTC on 1 January 2001.
-  public init(timeIntervalSinceReferenceDate: TimeInterval) {
-    let sd = floor(timeIntervalSinceReferenceDate)
-    let nd = round(
-      (timeIntervalSinceReferenceDate - sd) * TimeInterval(nanosPerSecond))
-    // The addition of timeIntervalBetween1970And... is deliberately delayed
-    // until the input is separated into an integer part and a fraction
-    // part, so that we don't unnecessarily lose precision.
-    let (s, n) = normalizeForTimestamp(
-      seconds: Int64(sd) + Int64(Date.timeIntervalBetween1970AndReferenceDate),
-      nanos: Int32(nd))
-    self.init(seconds: s, nanos: n)
-  }
-
-  /// Creates a new `Google_Protobuf_Timestamp` initialized to the same time as
-  /// the given `Date`.
-  ///
-  /// - Parameter date: The `Date` with which to initialize the timestamp.
-  public init(date: Date) {
-    // Note: Internally, Date uses the "reference date," not the 1970 date.
-    // We use it when interacting with Dates so that Date doesn't perform
-    // any double arithmetic on our behalf, which might cost us precision.
-    self.init(
-      timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate)
-  }
-
-  /// The interval between the timestamp and 00:00:00 UTC on 1 January 1970.
-  public var timeIntervalSince1970: TimeInterval {
-    return TimeInterval(self.seconds) +
-      TimeInterval(self.nanos) / TimeInterval(nanosPerSecond)
-  }
-
-  /// The interval between the timestamp and 00:00:00 UTC on 1 January 2001.
-  public var timeIntervalSinceReferenceDate: TimeInterval {
-    return TimeInterval(
-      self.seconds - Int64(Date.timeIntervalBetween1970AndReferenceDate)) +
-      TimeInterval(self.nanos) / TimeInterval(nanosPerSecond)
-  }
-
-  /// A `Date` initialized to the same time as the timestamp.
-  public var date: Date {
-    return Date(
-      timeIntervalSinceReferenceDate: self.timeIntervalSinceReferenceDate)
-  }
+    /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00
+    /// UTC on 1 January 1970 by a given number of seconds.
+    ///
+    /// - Parameter timeIntervalSince1970: The `TimeInterval`, interpreted as
+    ///   seconds relative to 00:00:00 UTC on 1 January 1970.
+    public init(timeIntervalSince1970: TimeInterval) {
+        let sd = floor(timeIntervalSince1970)
+        let nd = round((timeIntervalSince1970 - sd) * TimeInterval(nanosPerSecond))
+        let (s, n) = normalizeForTimestamp(seconds: Int64(sd), nanos: Int32(nd))
+        self.init(seconds: s, nanos: n)
+    }
+
+    /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00
+    /// UTC on 1 January 2001 by a given number of seconds.
+    ///
+    /// - Parameter timeIntervalSinceReferenceDate: The `TimeInterval`,
+    ///   interpreted as seconds relative to 00:00:00 UTC on 1 January 2001.
+    public init(timeIntervalSinceReferenceDate: TimeInterval) {
+        let sd = floor(timeIntervalSinceReferenceDate)
+        let nd = round(
+            (timeIntervalSinceReferenceDate - sd) * TimeInterval(nanosPerSecond)
+        )
+        // The addition of timeIntervalBetween1970And... is deliberately delayed
+        // until the input is separated into an integer part and a fraction
+        // part, so that we don't unnecessarily lose precision.
+        let (s, n) = normalizeForTimestamp(
+            seconds: Int64(sd) + Int64(Date.timeIntervalBetween1970AndReferenceDate),
+            nanos: Int32(nd)
+        )
+        self.init(seconds: s, nanos: n)
+    }
+
+    /// Creates a new `Google_Protobuf_Timestamp` initialized to the same time as
+    /// the given `Date`.
+    ///
+    /// - Parameter date: The `Date` with which to initialize the timestamp.
+    public init(date: Date) {
+        // Note: Internally, Date uses the "reference date," not the 1970 date.
+        // We use it when interacting with Dates so that Date doesn't perform
+        // any double arithmetic on our behalf, which might cost us precision.
+        self.init(
+            timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate
+        )
+    }
+
+    /// The interval between the timestamp and 00:00:00 UTC on 1 January 1970.
+    public var timeIntervalSince1970: TimeInterval {
+        TimeInterval(self.seconds) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond)
+    }
+
+    /// The interval between the timestamp and 00:00:00 UTC on 1 January 2001.
+    public var timeIntervalSinceReferenceDate: TimeInterval {
+        TimeInterval(
+            self.seconds - Int64(Date.timeIntervalBetween1970AndReferenceDate)
+        ) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond)
+    }
+
+    /// A `Date` initialized to the same time as the timestamp.
+    public var date: Date {
+        Date(
+            timeIntervalSinceReferenceDate: self.timeIntervalSinceReferenceDate
+        )
+    }
 }
 
 private func normalizeForTimestamp(
-  seconds: Int64,
-  nanos: Int32
+    seconds: Int64,
+    nanos: Int32
 ) -> (seconds: Int64, nanos: Int32) {
-  // The Timestamp spec says that nanos must be in the range [0, 999999999),
-  // as in actual modular arithmetic.
+    // The Timestamp spec says that nanos must be in the range [0, 999999999),
+    // as in actual modular arithmetic.
 
-  let s = seconds + Int64(div(nanos, nanosPerSecond))
-  let n = mod(nanos, nanosPerSecond)
-  return (seconds: s, nanos: n)
+    let s = seconds + Int64(div(nanos, nanosPerSecond))
+    let n = mod(nanos, nanosPerSecond)
+    return (seconds: s, nanos: n)
 }
 
 public func + (
-  lhs: Google_Protobuf_Timestamp,
-  rhs: Google_Protobuf_Duration
+    lhs: Google_Protobuf_Timestamp,
+    rhs: Google_Protobuf_Duration
 ) -> Google_Protobuf_Timestamp {
-  let (s, n) = normalizeForTimestamp(seconds: lhs.seconds + rhs.seconds,
-                                     nanos: lhs.nanos + rhs.nanos)
-  return Google_Protobuf_Timestamp(seconds: s, nanos: n)
+    let (s, n) = normalizeForTimestamp(
+        seconds: lhs.seconds + rhs.seconds,
+        nanos: lhs.nanos + rhs.nanos
+    )
+    return Google_Protobuf_Timestamp(seconds: s, nanos: n)
 }
 
 public func + (
-  lhs: Google_Protobuf_Duration,
-  rhs: Google_Protobuf_Timestamp
+    lhs: Google_Protobuf_Duration,
+    rhs: Google_Protobuf_Timestamp
 ) -> Google_Protobuf_Timestamp {
-  let (s, n) = normalizeForTimestamp(seconds: lhs.seconds + rhs.seconds,
-                                     nanos: lhs.nanos + rhs.nanos)
-  return Google_Protobuf_Timestamp(seconds: s, nanos: n)
+    let (s, n) = normalizeForTimestamp(
+        seconds: lhs.seconds + rhs.seconds,
+        nanos: lhs.nanos + rhs.nanos
+    )
+    return Google_Protobuf_Timestamp(seconds: s, nanos: n)
 }
 
 public func - (
-  lhs: Google_Protobuf_Timestamp,
-  rhs: Google_Protobuf_Duration
+    lhs: Google_Protobuf_Timestamp,
+    rhs: Google_Protobuf_Duration
 ) -> Google_Protobuf_Timestamp {
-  let (s, n) = normalizeForTimestamp(seconds: lhs.seconds - rhs.seconds,
-                                     nanos: lhs.nanos - rhs.nanos)
-  return Google_Protobuf_Timestamp(seconds: s, nanos: n)
+    let (s, n) = normalizeForTimestamp(
+        seconds: lhs.seconds - rhs.seconds,
+        nanos: lhs.nanos - rhs.nanos
+    )
+    return Google_Protobuf_Timestamp(seconds: s, nanos: n)
 }

+ 129 - 129
Sources/SwiftProtobuf/Google_Protobuf_Value+Extensions.swift

@@ -14,154 +14,154 @@
 // -----------------------------------------------------------------------------
 
 extension Google_Protobuf_Value: ExpressibleByIntegerLiteral {
-  public typealias IntegerLiteralType = Int64
+    public typealias IntegerLiteralType = Int64
 
-  /// Creates a new `Google_Protobuf_Value` from an integer literal.
-  public init(integerLiteral value: Int64) {
-    self.init(kind: .numberValue(Double(value)))
-  }
+    /// Creates a new `Google_Protobuf_Value` from an integer literal.
+    public init(integerLiteral value: Int64) {
+        self.init(kind: .numberValue(Double(value)))
+    }
 }
 
 extension Google_Protobuf_Value: ExpressibleByFloatLiteral {
-  public typealias FloatLiteralType = Double
+    public typealias FloatLiteralType = Double
 
-  /// Creates a new `Google_Protobuf_Value` from a floating point literal.
-  public init(floatLiteral value: Double) {
-    self.init(kind: .numberValue(value))
-  }
+    /// Creates a new `Google_Protobuf_Value` from a floating point literal.
+    public init(floatLiteral value: Double) {
+        self.init(kind: .numberValue(value))
+    }
 }
 
 extension Google_Protobuf_Value: ExpressibleByBooleanLiteral {
-  public typealias BooleanLiteralType = Bool
+    public typealias BooleanLiteralType = Bool
 
-  /// Creates a new `Google_Protobuf_Value` from a boolean literal.
-  public init(booleanLiteral value: Bool) {
-    self.init(kind: .boolValue(value))
-  }
+    /// Creates a new `Google_Protobuf_Value` from a boolean literal.
+    public init(booleanLiteral value: Bool) {
+        self.init(kind: .boolValue(value))
+    }
 }
 
 extension Google_Protobuf_Value: ExpressibleByStringLiteral {
-  public typealias StringLiteralType = String
-  public typealias ExtendedGraphemeClusterLiteralType = String
-  public typealias UnicodeScalarLiteralType = String
-
-  /// Creates a new `Google_Protobuf_Value` from a string literal.
-  public init(stringLiteral value: String) {
-    self.init(kind: .stringValue(value))
-  }
-
-  /// Creates a new `Google_Protobuf_Value` from a Unicode scalar literal.
-  public init(unicodeScalarLiteral value: String) {
-    self.init(kind: .stringValue(value))
-  }
-
-  /// Creates a new `Google_Protobuf_Value` from a character literal.
-  public init(extendedGraphemeClusterLiteral value: String) {
-    self.init(kind: .stringValue(value))
-  }
+    public typealias StringLiteralType = String
+    public typealias ExtendedGraphemeClusterLiteralType = String
+    public typealias UnicodeScalarLiteralType = String
+
+    /// Creates a new `Google_Protobuf_Value` from a string literal.
+    public init(stringLiteral value: String) {
+        self.init(kind: .stringValue(value))
+    }
+
+    /// Creates a new `Google_Protobuf_Value` from a Unicode scalar literal.
+    public init(unicodeScalarLiteral value: String) {
+        self.init(kind: .stringValue(value))
+    }
+
+    /// Creates a new `Google_Protobuf_Value` from a character literal.
+    public init(extendedGraphemeClusterLiteral value: String) {
+        self.init(kind: .stringValue(value))
+    }
 }
 
 extension Google_Protobuf_Value: ExpressibleByNilLiteral {
-  /// Creates a new `Google_Protobuf_Value` from the nil literal.
-  public init(nilLiteral: ()) {
-    self.init(kind: .nullValue(.nullValue))
-  }
+    /// Creates a new `Google_Protobuf_Value` from the nil literal.
+    public init(nilLiteral: ()) {
+        self.init(kind: .nullValue(.nullValue))
+    }
 }
 
 extension Google_Protobuf_Value: _CustomJSONCodable {
-  internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    var jsonEncoder = JSONEncoder()
-    try serializeJSONValue(to: &jsonEncoder, options: options)
-    return jsonEncoder.stringResult
-  }
-
-  internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    let c = try decoder.scanner.peekOneCharacter()
-    switch c {
-    case "n":
-      if !decoder.scanner.skipOptionalNull() {
-        throw JSONDecodingError.failure
-      }
-      kind = .nullValue(.nullValue)
-    case "[":
-      var l = Google_Protobuf_ListValue()
-      try l.decodeJSON(from: &decoder)
-      kind = .listValue(l)
-    case "{":
-      var s = Google_Protobuf_Struct()
-      try s.decodeJSON(from: &decoder)
-      kind = .structValue(s)
-    case "t", "f":
-      let b = try decoder.scanner.nextBool()
-      kind = .boolValue(b)
-    case "\"":
-      let s = try decoder.scanner.nextQuotedString()
-      kind = .stringValue(s)
-    default:
-      let d = try decoder.scanner.nextDouble()
-      kind = .numberValue(d)
-    }
-  }
-
-  internal static func decodedFromJSONNull() -> Google_Protobuf_Value? {
-    return Google_Protobuf_Value(kind: .nullValue(.nullValue))
-  }
+    internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        var jsonEncoder = JSONEncoder()
+        try serializeJSONValue(to: &jsonEncoder, options: options)
+        return jsonEncoder.stringResult
+    }
+
+    internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        let c = try decoder.scanner.peekOneCharacter()
+        switch c {
+        case "n":
+            if !decoder.scanner.skipOptionalNull() {
+                throw JSONDecodingError.failure
+            }
+            kind = .nullValue(.nullValue)
+        case "[":
+            var l = Google_Protobuf_ListValue()
+            try l.decodeJSON(from: &decoder)
+            kind = .listValue(l)
+        case "{":
+            var s = Google_Protobuf_Struct()
+            try s.decodeJSON(from: &decoder)
+            kind = .structValue(s)
+        case "t", "f":
+            let b = try decoder.scanner.nextBool()
+            kind = .boolValue(b)
+        case "\"":
+            let s = try decoder.scanner.nextQuotedString()
+            kind = .stringValue(s)
+        default:
+            let d = try decoder.scanner.nextDouble()
+            kind = .numberValue(d)
+        }
+    }
+
+    internal static func decodedFromJSONNull() -> Google_Protobuf_Value? {
+        Google_Protobuf_Value(kind: .nullValue(.nullValue))
+    }
 }
 
 extension Google_Protobuf_Value {
-  /// Creates a new `Google_Protobuf_Value` with the given kind.
-  fileprivate init(kind: OneOf_Kind) {
-    self.init()
-    self.kind = kind
-  }
-
-  /// Creates a new `Google_Protobuf_Value` whose `kind` is `numberValue` with
-  /// the given floating-point value.
-  public init(numberValue: Double) {
-    self.init(kind: .numberValue(numberValue))
-  }
-
-  /// Creates a new `Google_Protobuf_Value` whose `kind` is `stringValue` with
-  /// the given string value.
-  public init(stringValue: String) {
-    self.init(kind: .stringValue(stringValue))
-  }
-
-  /// Creates a new `Google_Protobuf_Value` whose `kind` is `boolValue` with the
-  /// given boolean value.
-  public init(boolValue: Bool) {
-    self.init(kind: .boolValue(boolValue))
-  }
-
-  /// Creates a new `Google_Protobuf_Value` whose `kind` is `structValue` with
-  /// the given `Google_Protobuf_Struct` value.
-  public init(structValue: Google_Protobuf_Struct) {
-    self.init(kind: .structValue(structValue))
-  }
-
-  /// Creates a new `Google_Protobuf_Value` whose `kind` is `listValue` with the
-  /// given `Google_Struct_ListValue` value.
-  public init(listValue: Google_Protobuf_ListValue) {
-    self.init(kind: .listValue(listValue))
-  }
-
-  /// Writes out the JSON representation of the value to the given encoder.
-  internal func serializeJSONValue(
-    to encoder: inout JSONEncoder,
-    options: JSONEncodingOptions
-  ) throws {
-    switch kind {
-    case .nullValue?: encoder.putNullValue()
-    case .numberValue(let v)?:
-      guard v.isFinite else {
-        throw JSONEncodingError.valueNumberNotFinite
-      }
-      encoder.putDoubleValue(value: v)
-    case .stringValue(let v)?: encoder.putStringValue(value: v)
-    case .boolValue(let v)?: encoder.putNonQuotedBoolValue(value: v)
-    case .structValue(let v)?: encoder.append(text: try v.jsonString(options: options))
-    case .listValue(let v)?: encoder.append(text: try v.jsonString(options: options))
-    case nil: throw JSONEncodingError.missingValue
-    }
-  }
+    /// Creates a new `Google_Protobuf_Value` with the given kind.
+    fileprivate init(kind: OneOf_Kind) {
+        self.init()
+        self.kind = kind
+    }
+
+    /// Creates a new `Google_Protobuf_Value` whose `kind` is `numberValue` with
+    /// the given floating-point value.
+    public init(numberValue: Double) {
+        self.init(kind: .numberValue(numberValue))
+    }
+
+    /// Creates a new `Google_Protobuf_Value` whose `kind` is `stringValue` with
+    /// the given string value.
+    public init(stringValue: String) {
+        self.init(kind: .stringValue(stringValue))
+    }
+
+    /// Creates a new `Google_Protobuf_Value` whose `kind` is `boolValue` with the
+    /// given boolean value.
+    public init(boolValue: Bool) {
+        self.init(kind: .boolValue(boolValue))
+    }
+
+    /// Creates a new `Google_Protobuf_Value` whose `kind` is `structValue` with
+    /// the given `Google_Protobuf_Struct` value.
+    public init(structValue: Google_Protobuf_Struct) {
+        self.init(kind: .structValue(structValue))
+    }
+
+    /// Creates a new `Google_Protobuf_Value` whose `kind` is `listValue` with the
+    /// given `Google_Struct_ListValue` value.
+    public init(listValue: Google_Protobuf_ListValue) {
+        self.init(kind: .listValue(listValue))
+    }
+
+    /// Writes out the JSON representation of the value to the given encoder.
+    internal func serializeJSONValue(
+        to encoder: inout JSONEncoder,
+        options: JSONEncodingOptions
+    ) throws {
+        switch kind {
+        case .nullValue?: encoder.putNullValue()
+        case .numberValue(let v)?:
+            guard v.isFinite else {
+                throw JSONEncodingError.valueNumberNotFinite
+            }
+            encoder.putDoubleValue(value: v)
+        case .stringValue(let v)?: encoder.putStringValue(value: v)
+        case .boolValue(let v)?: encoder.putNonQuotedBoolValue(value: v)
+        case .structValue(let v)?: encoder.append(text: try v.jsonString(options: options))
+        case .listValue(let v)?: encoder.append(text: try v.jsonString(options: options))
+        case nil: throw JSONEncodingError.missingValue
+        }
+    }
 }

+ 183 - 175
Sources/SwiftProtobuf/Google_Protobuf_Wrappers+Extensions.swift

@@ -19,237 +19,245 @@ import Foundation
 /// wrapper types extended below.
 protocol ProtobufWrapper {
 
-  /// The wrapped protobuf type (for example, `ProtobufDouble`).
-  associatedtype WrappedType: FieldType
+    /// The wrapped protobuf type (for example, `ProtobufDouble`).
+    associatedtype WrappedType: FieldType
 
-  /// Exposes the generated property to the extensions here.
-  var value: WrappedType.BaseType { get set }
+    /// Exposes the generated property to the extensions here.
+    var value: WrappedType.BaseType { get set }
 
-  /// Exposes the parameterless initializer to the extensions here.
-  init()
+    /// Exposes the parameterless initializer to the extensions here.
+    init()
 
-  /// Creates a new instance of the wrapper with the given value.
-  init(_ value: WrappedType.BaseType)
+    /// Creates a new instance of the wrapper with the given value.
+    init(_ value: WrappedType.BaseType)
 }
 
 extension ProtobufWrapper {
-  mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
-    var v: WrappedType.BaseType?
-    try WrappedType.decodeSingular(value: &v, from: &decoder)
-    value = v ?? WrappedType.proto3DefaultValue
-  }
+    mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
+        var v: WrappedType.BaseType?
+        try WrappedType.decodeSingular(value: &v, from: &decoder)
+        value = v ?? WrappedType.proto3DefaultValue
+    }
 }
 
 extension Google_Protobuf_DoubleValue:
-  ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable {
-
-  public typealias WrappedType = ProtobufDouble
-  public typealias FloatLiteralType = WrappedType.BaseType
-
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
-
-  public init(floatLiteral: FloatLiteralType) {
-    self.init(floatLiteral)
-  }
-
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    if value.isFinite {
-      // Swift 4.2 and later guarantees that this is accurate
-      // enough to parse back to the exact value on the other end.
-      return value.description
-    } else {
-      // Protobuf-specific handling of NaN and infinities
-      var encoder = JSONEncoder()
-      encoder.putDoubleValue(value: value)
-      return encoder.stringResult
-    }
-  }
+    ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable
+{
+
+    public typealias WrappedType = ProtobufDouble
+    public typealias FloatLiteralType = WrappedType.BaseType
+
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
+
+    public init(floatLiteral: FloatLiteralType) {
+        self.init(floatLiteral)
+    }
+
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        if value.isFinite {
+            // Swift 4.2 and later guarantees that this is accurate
+            // enough to parse back to the exact value on the other end.
+            return value.description
+        } else {
+            // Protobuf-specific handling of NaN and infinities
+            var encoder = JSONEncoder()
+            encoder.putDoubleValue(value: value)
+            return encoder.stringResult
+        }
+    }
 }
 
 extension Google_Protobuf_FloatValue:
-  ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable {
-
-  public typealias WrappedType = ProtobufFloat
-  public typealias FloatLiteralType = Float
-
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
-
-  public init(floatLiteral: FloatLiteralType) {
-    self.init(floatLiteral)
-  }
-
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    if value.isFinite {
-      // Swift 4.2 and later guarantees that this is accurate
-      // enough to parse back to the exact value on the other end.
-      return value.description
-    } else {
-      // Protobuf-specific handling of NaN and infinities
-      var encoder = JSONEncoder()
-      encoder.putFloatValue(value: value)
-      return encoder.stringResult
-    }
-  }
+    ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable
+{
+
+    public typealias WrappedType = ProtobufFloat
+    public typealias FloatLiteralType = Float
+
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
+
+    public init(floatLiteral: FloatLiteralType) {
+        self.init(floatLiteral)
+    }
+
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        if value.isFinite {
+            // Swift 4.2 and later guarantees that this is accurate
+            // enough to parse back to the exact value on the other end.
+            return value.description
+        } else {
+            // Protobuf-specific handling of NaN and infinities
+            var encoder = JSONEncoder()
+            encoder.putFloatValue(value: value)
+            return encoder.stringResult
+        }
+    }
 }
 
 extension Google_Protobuf_Int64Value:
-  ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable {
+    ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable
+{
 
-  public typealias WrappedType = ProtobufInt64
-  public typealias IntegerLiteralType = WrappedType.BaseType
+    public typealias WrappedType = ProtobufInt64
+    public typealias IntegerLiteralType = WrappedType.BaseType
 
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
 
-  public init(integerLiteral: IntegerLiteralType) {
-    self.init(integerLiteral)
-  }
+    public init(integerLiteral: IntegerLiteralType) {
+        self.init(integerLiteral)
+    }
 
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    var encoded = value.description
-    if !options.alwaysPrintInt64sAsNumbers {
-      encoded = "\"" + encoded + "\""
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        var encoded = value.description
+        if !options.alwaysPrintInt64sAsNumbers {
+            encoded = "\"" + encoded + "\""
+        }
+        return encoded
     }
-    return encoded
-  }
 }
 
 extension Google_Protobuf_UInt64Value:
-  ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable {
+    ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable
+{
 
-  public typealias WrappedType = ProtobufUInt64
-  public typealias IntegerLiteralType = WrappedType.BaseType
+    public typealias WrappedType = ProtobufUInt64
+    public typealias IntegerLiteralType = WrappedType.BaseType
 
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
 
-  public init(integerLiteral: IntegerLiteralType) {
-    self.init(integerLiteral)
-  }
+    public init(integerLiteral: IntegerLiteralType) {
+        self.init(integerLiteral)
+    }
 
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    var encoded = String(value)
-    if !options.alwaysPrintInt64sAsNumbers {
-      encoded = "\"" + encoded + "\""
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        var encoded = String(value)
+        if !options.alwaysPrintInt64sAsNumbers {
+            encoded = "\"" + encoded + "\""
+        }
+        return encoded
     }
-    return encoded
-  }
 }
 
 extension Google_Protobuf_Int32Value:
-  ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable {
+    ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable
+{
 
-  public typealias WrappedType = ProtobufInt32
-  public typealias IntegerLiteralType = WrappedType.BaseType
+    public typealias WrappedType = ProtobufInt32
+    public typealias IntegerLiteralType = WrappedType.BaseType
 
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
 
-  public init(integerLiteral: IntegerLiteralType) {
-    self.init(integerLiteral)
-  }
+    public init(integerLiteral: IntegerLiteralType) {
+        self.init(integerLiteral)
+    }
 
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    return String(value)
-  }
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        String(value)
+    }
 }
 
 extension Google_Protobuf_UInt32Value:
-  ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable {
+    ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable
+{
 
-  public typealias WrappedType = ProtobufUInt32
-  public typealias IntegerLiteralType = WrappedType.BaseType
+    public typealias WrappedType = ProtobufUInt32
+    public typealias IntegerLiteralType = WrappedType.BaseType
 
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
 
-  public init(integerLiteral: IntegerLiteralType) {
-    self.init(integerLiteral)
-  }
+    public init(integerLiteral: IntegerLiteralType) {
+        self.init(integerLiteral)
+    }
 
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    return String(value)
-  }
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        String(value)
+    }
 }
 
 extension Google_Protobuf_BoolValue:
-  ProtobufWrapper, ExpressibleByBooleanLiteral, _CustomJSONCodable {
+    ProtobufWrapper, ExpressibleByBooleanLiteral, _CustomJSONCodable
+{
 
-  public typealias WrappedType = ProtobufBool
-  public typealias BooleanLiteralType = Bool
+    public typealias WrappedType = ProtobufBool
+    public typealias BooleanLiteralType = Bool
 
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
 
-  public init(booleanLiteral: Bool) {
-    self.init(booleanLiteral)
-  }
+    public init(booleanLiteral: Bool) {
+        self.init(booleanLiteral)
+    }
 
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    return value ? "true" : "false"
-  }
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        value ? "true" : "false"
+    }
 }
 
 extension Google_Protobuf_StringValue:
-  ProtobufWrapper, ExpressibleByStringLiteral, _CustomJSONCodable {
-
-  public typealias WrappedType = ProtobufString
-  public typealias StringLiteralType = String
-  public typealias ExtendedGraphemeClusterLiteralType = String
-  public typealias UnicodeScalarLiteralType = String
-
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
-
-  public init(stringLiteral: String) {
-    self.init(stringLiteral)
-  }
-
-  public init(extendedGraphemeClusterLiteral: String) {
-    self.init(extendedGraphemeClusterLiteral)
-  }
-
-  public init(unicodeScalarLiteral: String) {
-    self.init(unicodeScalarLiteral)
-  }
-
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    var encoder = JSONEncoder()
-    encoder.putStringValue(value: value)
-    return encoder.stringResult
-  }
+    ProtobufWrapper, ExpressibleByStringLiteral, _CustomJSONCodable
+{
+
+    public typealias WrappedType = ProtobufString
+    public typealias StringLiteralType = String
+    public typealias ExtendedGraphemeClusterLiteralType = String
+    public typealias UnicodeScalarLiteralType = String
+
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
+
+    public init(stringLiteral: String) {
+        self.init(stringLiteral)
+    }
+
+    public init(extendedGraphemeClusterLiteral: String) {
+        self.init(extendedGraphemeClusterLiteral)
+    }
+
+    public init(unicodeScalarLiteral: String) {
+        self.init(unicodeScalarLiteral)
+    }
+
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        var encoder = JSONEncoder()
+        encoder.putStringValue(value: value)
+        return encoder.stringResult
+    }
 }
 
 extension Google_Protobuf_BytesValue: ProtobufWrapper, _CustomJSONCodable {
 
-  public typealias WrappedType = ProtobufBytes
+    public typealias WrappedType = ProtobufBytes
 
-  public init(_ value: WrappedType.BaseType) {
-    self.init()
-    self.value = value
-  }
+    public init(_ value: WrappedType.BaseType) {
+        self.init()
+        self.value = value
+    }
 
-  func encodedJSONString(options: JSONEncodingOptions) throws -> String {
-    var encoder = JSONEncoder()
-    encoder.putBytesValue(value: value)
-    return encoder.stringResult
-  }
+    func encodedJSONString(options: JSONEncodingOptions) throws -> String {
+        var encoder = JSONEncoder()
+        encoder.putBytesValue(value: value)
+        return encoder.stringResult
+    }
 }

+ 193 - 191
Sources/SwiftProtobuf/HashVisitor.swift

@@ -15,8 +15,8 @@
 
 import Foundation
 
-private let i_2166136261 = Int(bitPattern: 2166136261)
-private let i_16777619 = Int(16777619)
+private let i_2166136261 = Int(bitPattern: 2_166_136_261)
+private let i_16777619 = Int(16_777_619)
 
 /// Computes the hash of a message by visiting its fields recursively.
 ///
@@ -27,193 +27,195 @@ private let i_16777619 = Int(16777619)
 /// message fields they want to include.
 internal struct HashVisitor: Visitor {
 
-  internal private(set) var hasher: Hasher
-
-  init(_ hasher: Hasher) {
-    self.hasher = hasher
-  }
-
-  mutating func visitUnknown(bytes: Data) throws {
-    hasher.combine(bytes)
-  }
-
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitSingularEnumField<E: Enum>(value: E,
-                                                fieldNumber: Int) {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) {
-    hasher.combine(fieldNumber)
-    value.hash(into: &hasher)
-  }
-
-  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    for v in value {
-      v.hash(into: &hasher)
-    }
-  }
-
-  mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    hasher.combine(fieldNumber)
-    for v in value {
-      v.hash(into: &hasher)
-    }
-  }
-
-  mutating func visitMapField<KeyType, ValueType: MapValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: _ProtobufMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where ValueType.RawValue == Int {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-    hasher.combine(fieldNumber)
-    hasher.combine(value)
-  }
+    internal private(set) var hasher: Hasher
+
+    init(_ hasher: Hasher) {
+        self.hasher = hasher
+    }
+
+    mutating func visitUnknown(bytes: Data) throws {
+        hasher.combine(bytes)
+    }
+
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitSingularEnumField<E: Enum>(
+        value: E,
+        fieldNumber: Int
+    ) {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) {
+        hasher.combine(fieldNumber)
+        value.hash(into: &hasher)
+    }
+
+    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        for v in value {
+            v.hash(into: &hasher)
+        }
+    }
+
+    mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        hasher.combine(fieldNumber)
+        for v in value {
+            v.hash(into: &hasher)
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where ValueType.RawValue == Int {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        hasher.combine(fieldNumber)
+        hasher.combine(value)
+    }
 }

+ 32 - 27
Sources/SwiftProtobuf/Internal.swift

@@ -19,35 +19,40 @@ import Foundation
 /// Functions that are public only because they are used by generated message implementations.
 /// - Important: NOT INTENDED TO BE CALLED BY CLIENTS.
 public enum Internal {
-    
-  /// A singleton instance of an empty data that is used by the generated code
-  /// for default values. This is a performance enhancement to work around the
-  /// fact that the `Data` type in Swift involves a new heap allocation every
-  /// time an empty instance is initialized, instead of sharing a common empty
-  /// backing storage.
-  /// - Note: This isn't really used any longer - it's only here to support code generated by 1.10.2 and earlier.
-  @available(*, deprecated, message: "Internal.emptyData isn't used any longer in newer versions of the generator. Generate code with a version later than 1.10.2 to get performance improvements. See https://github.com/apple/swift-protobuf/pull/1028 for more information.")
-  public static let emptyData = Data()
 
-  /// Helper to loop over a list of Messages to see if they are all
-  /// initialized (see Message.isInitialized for what that means).
-  public static func areAllInitialized(_ listOfMessages: [any Message]) -> Bool {
-    for msg in listOfMessages {
-      if !msg.isInitialized {
-        return false
-      }
+    /// A singleton instance of an empty data that is used by the generated code
+    /// for default values. This is a performance enhancement to work around the
+    /// fact that the `Data` type in Swift involves a new heap allocation every
+    /// time an empty instance is initialized, instead of sharing a common empty
+    /// backing storage.
+    /// - Note: This isn't really used any longer - it's only here to support code generated by 1.10.2 and earlier.
+    @available(
+        *,
+        deprecated,
+        message:
+            "Internal.emptyData isn't used any longer in newer versions of the generator. Generate code with a version later than 1.10.2 to get performance improvements. See https://github.com/apple/swift-protobuf/pull/1028 for more information."
+    )
+    public static let emptyData = Data()
+
+    /// Helper to loop over a list of Messages to see if they are all
+    /// initialized (see Message.isInitialized for what that means).
+    public static func areAllInitialized(_ listOfMessages: [any Message]) -> Bool {
+        for msg in listOfMessages {
+            if !msg.isInitialized {
+                return false
+            }
+        }
+        return true
     }
-    return true
-  }
 
-  /// Helper to loop over dictionary with values that are Messages to see if
-  /// they are all initialized (see Message.isInitialized for what that means).
-  public static func areAllInitialized<K>(_ mapToMessages: [K: any Message]) -> Bool {
-    for (_, msg) in mapToMessages {
-      if !msg.isInitialized {
-        return false
-      }
+    /// Helper to loop over dictionary with values that are Messages to see if
+    /// they are all initialized (see Message.isInitialized for what that means).
+    public static func areAllInitialized<K>(_ mapToMessages: [K: any Message]) -> Bool {
+        for (_, msg) in mapToMessages {
+            if !msg.isInitialized {
+                return false
+            }
+        }
+        return true
     }
-    return true
-  }
 }

+ 731 - 722
Sources/SwiftProtobuf/JSONDecoder.swift

@@ -15,733 +15,742 @@
 import Foundation
 
 internal struct JSONDecoder: Decoder {
-  internal var scanner: JSONScanner
-  internal var messageType: any Message.Type
-  private var fieldCount = 0
-  private var isMapKey = false
-  private var fieldNameMap: _NameMap?
-
-  internal var options: JSONDecodingOptions {
-    return scanner.options
-  }
-
-  mutating func handleConflictingOneOf() throws {
-    throw JSONDecodingError.conflictingOneOf
-  }
-
-  internal init(source: UnsafeRawBufferPointer, options: JSONDecodingOptions,
-                messageType: any Message.Type, extensions: (any ExtensionMap)?) {
-    let scanner = JSONScanner(source: source,
-                               options: options,
-                               extensions: extensions)
-    self.init(scanner: scanner, messageType: messageType)
-  }
-
-  private init(scanner: JSONScanner, messageType: any Message.Type) {
-    self.scanner = scanner
-    self.messageType = messageType
-  }
-
-  mutating func nextFieldNumber() throws -> Int? {
-    if scanner.skipOptionalObjectEnd() {
-      return nil
-    }
-    if fieldCount > 0 {
-      try scanner.skipRequiredComma()
-    }
-    let fieldNumber = try scanner.nextFieldNumber(names: fieldNameMap!,
-                                                  messageType: messageType)
-    if let fieldNumber = fieldNumber {
-      fieldCount += 1
-      return fieldNumber
-    }
-    return nil
-  }
-
-  mutating func decodeSingularFloatField(value: inout Float) throws {
-    if scanner.skipOptionalNull() {
-      value = 0
-      return
-    }
-    value = try scanner.nextFloat()
-  }
-
-  mutating func decodeSingularFloatField(value: inout Float?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    value = try scanner.nextFloat()
-  }
-
-  mutating func decodeRepeatedFloatField(value: inout [Float]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextFloat()
-      value.append(n)
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularDoubleField(value: inout Double) throws {
-    if scanner.skipOptionalNull() {
-      value = 0
-      return
-    }
-    value = try scanner.nextDouble()
-  }
-
-  mutating func decodeSingularDoubleField(value: inout Double?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    value = try scanner.nextDouble()
-  }
-
-  mutating func decodeRepeatedDoubleField(value: inout [Double]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextDouble()
-      value.append(n)
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularInt32Field(value: inout Int32) throws {
-    if scanner.skipOptionalNull() {
-      value = 0
-      return
-    }
-    let n = try scanner.nextSInt()
-    if n > Int64(Int32.max) || n < Int64(Int32.min) {
-      throw JSONDecodingError.numberRange
-    }
-    value = Int32(truncatingIfNeeded: n)
-  }
-
-  mutating func decodeSingularInt32Field(value: inout Int32?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    let n = try scanner.nextSInt()
-    if n > Int64(Int32.max) || n < Int64(Int32.min) {
-      throw JSONDecodingError.numberRange
-    }
-    value = Int32(truncatingIfNeeded: n)
-  }
-
-  mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextSInt()
-      if n > Int64(Int32.max) || n < Int64(Int32.min) {
-        throw JSONDecodingError.numberRange
-      }
-      value.append(Int32(truncatingIfNeeded: n))
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularInt64Field(value: inout Int64) throws {
-    if scanner.skipOptionalNull() {
-      value = 0
-      return
-    }
-    value = try scanner.nextSInt()
-  }
-
-  mutating func decodeSingularInt64Field(value: inout Int64?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    value = try scanner.nextSInt()
-  }
-
-  mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextSInt()
-      value.append(n)
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularUInt32Field(value: inout UInt32) throws {
-    if scanner.skipOptionalNull() {
-      value = 0
-      return
-    }
-    let n = try scanner.nextUInt()
-    if n > UInt64(UInt32.max) {
-      throw JSONDecodingError.numberRange
-    }
-    value = UInt32(truncatingIfNeeded: n)
-  }
-
-  mutating func decodeSingularUInt32Field(value: inout UInt32?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    let n = try scanner.nextUInt()
-    if n > UInt64(UInt32.max) {
-      throw JSONDecodingError.numberRange
-    }
-    value = UInt32(truncatingIfNeeded: n)
-  }
-
-  mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextUInt()
-      if n > UInt64(UInt32.max) {
-        throw JSONDecodingError.numberRange
-      }
-      value.append(UInt32(truncatingIfNeeded: n))
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularUInt64Field(value: inout UInt64) throws {
-    if scanner.skipOptionalNull() {
-      value = 0
-      return
-    }
-    value = try scanner.nextUInt()
-  }
-
-  mutating func decodeSingularUInt64Field(value: inout UInt64?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    value = try scanner.nextUInt()
-  }
-
-  mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextUInt()
-      value.append(n)
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularSInt32Field(value: inout Int32) throws {
-    try decodeSingularInt32Field(value: &value)
-  }
-
-  mutating func decodeSingularSInt32Field(value: inout Int32?) throws {
-    try decodeSingularInt32Field(value: &value)
-  }
-
-  mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws {
-    try decodeRepeatedInt32Field(value: &value)
-  }
-
-  mutating func decodeSingularSInt64Field(value: inout Int64) throws {
-    try decodeSingularInt64Field(value: &value)
-  }
-
-  mutating func decodeSingularSInt64Field(value: inout Int64?) throws {
-    try decodeSingularInt64Field(value: &value)
-  }
-
-  mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws {
-    try decodeRepeatedInt64Field(value: &value)
-  }
-
-  mutating func decodeSingularFixed32Field(value: inout UInt32) throws {
-    try decodeSingularUInt32Field(value: &value)
-  }
-
-  mutating func decodeSingularFixed32Field(value: inout UInt32?) throws {
-    try decodeSingularUInt32Field(value: &value)
-  }
-
-  mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws {
-    try decodeRepeatedUInt32Field(value: &value)
-  }
-
-  mutating func decodeSingularFixed64Field(value: inout UInt64) throws {
-    try decodeSingularUInt64Field(value: &value)
-  }
-
-  mutating func decodeSingularFixed64Field(value: inout UInt64?) throws {
-    try decodeSingularUInt64Field(value: &value)
-  }
-
-  mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws {
-    try decodeRepeatedUInt64Field(value: &value)
-  }
-
-  mutating func decodeSingularSFixed32Field(value: inout Int32) throws {
-    try decodeSingularInt32Field(value: &value)
-  }
-
-  mutating func decodeSingularSFixed32Field(value: inout Int32?) throws {
-    try decodeSingularInt32Field(value: &value)
-  }
-
-  mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws {
-    try decodeRepeatedInt32Field(value: &value)
-  }
-
-  mutating func decodeSingularSFixed64Field(value: inout Int64) throws {
-    try decodeSingularInt64Field(value: &value)
-  }
+    internal var scanner: JSONScanner
+    internal var messageType: any Message.Type
+    private var fieldCount = 0
+    private var isMapKey = false
+    private var fieldNameMap: _NameMap?
 
-  mutating func decodeSingularSFixed64Field(value: inout Int64?) throws {
-    try decodeSingularInt64Field(value: &value)
-  }
-
-  mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws {
-    try decodeRepeatedInt64Field(value: &value)
-  }
-
-  mutating func decodeSingularBoolField(value: inout Bool) throws {
-    if scanner.skipOptionalNull() {
-      value = false
-      return
-    }
-    if isMapKey {
-      value = try scanner.nextQuotedBool()
-    } else {
-      value = try scanner.nextBool()
-    }
-  }
-
-  mutating func decodeSingularBoolField(value: inout Bool?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    if isMapKey {
-      value = try scanner.nextQuotedBool()
-    } else {
-      value = try scanner.nextBool()
-    }
-  }
-
-  mutating func decodeRepeatedBoolField(value: inout [Bool]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextBool()
-      value.append(n)
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularStringField(value: inout String) throws {
-    if scanner.skipOptionalNull() {
-      value = String()
-      return
-    }
-    value = try scanner.nextQuotedString()
-  }
-
-  mutating func decodeSingularStringField(value: inout String?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    value = try scanner.nextQuotedString()
-  }
-
-  mutating func decodeRepeatedStringField(value: inout [String]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextQuotedString()
-      value.append(n)
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularBytesField(value: inout Data) throws {
-    if scanner.skipOptionalNull() {
-      value = Data()
-      return
-    }
-    value = try scanner.nextBytesValue()
-  }
-
-  mutating func decodeSingularBytesField(value: inout Data?) throws {
-    if scanner.skipOptionalNull() {
-      value = nil
-      return
-    }
-    value = try scanner.nextBytesValue()
-  }
-
-  mutating func decodeRepeatedBytesField(value: inout [Data]) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      let n = try scanner.nextBytesValue()
-      value.append(n)
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularEnumField<E: Enum>(value: inout E?) throws
-  where E.RawValue == Int {
-    if scanner.skipOptionalNull() {
-      if let customDecodable = E.self as? any _CustomJSONCodable.Type {
-        value = try customDecodable.decodedFromJSONNull() as? E
-        return
-      }
-      value = nil
-      return
-    }
-    // Only change the value if a value was read.
-    if let e: E = try scanner.nextEnumValue() {
-      value = e
-    }
-  }
-
-  mutating func decodeSingularEnumField<E: Enum>(value: inout E) throws
-  where E.RawValue == Int {
-    if scanner.skipOptionalNull() {
-      if let customDecodable = E.self as? any _CustomJSONCodable.Type {
-        value = try customDecodable.decodedFromJSONNull() as! E
-        return
-      }
-      value = E()
-      return
-    }
-    if let e: E = try scanner.nextEnumValue() {
-      value = e
-    }
-
-  }
-
-  mutating func decodeRepeatedEnumField<E: Enum>(value: inout [E]) throws
-  where E.RawValue == Int {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    let maybeCustomDecodable = E.self as? any _CustomJSONCodable.Type
-    while true {
-      if scanner.skipOptionalNull() {
-        if let customDecodable = maybeCustomDecodable {
-          let e = try customDecodable.decodedFromJSONNull() as! E
-          value.append(e)
+    internal var options: JSONDecodingOptions {
+        scanner.options
+    }
+
+    mutating func handleConflictingOneOf() throws {
+        throw JSONDecodingError.conflictingOneOf
+    }
+
+    internal init(
+        source: UnsafeRawBufferPointer,
+        options: JSONDecodingOptions,
+        messageType: any Message.Type,
+        extensions: (any ExtensionMap)?
+    ) {
+        let scanner = JSONScanner(
+            source: source,
+            options: options,
+            extensions: extensions
+        )
+        self.init(scanner: scanner, messageType: messageType)
+    }
+
+    private init(scanner: JSONScanner, messageType: any Message.Type) {
+        self.scanner = scanner
+        self.messageType = messageType
+    }
+
+    mutating func nextFieldNumber() throws -> Int? {
+        if scanner.skipOptionalObjectEnd() {
+            return nil
+        }
+        if fieldCount > 0 {
+            try scanner.skipRequiredComma()
+        }
+        let fieldNumber = try scanner.nextFieldNumber(
+            names: fieldNameMap!,
+            messageType: messageType
+        )
+        if let fieldNumber = fieldNumber {
+            fieldCount += 1
+            return fieldNumber
+        }
+        return nil
+    }
+
+    mutating func decodeSingularFloatField(value: inout Float) throws {
+        if scanner.skipOptionalNull() {
+            value = 0
+            return
+        }
+        value = try scanner.nextFloat()
+    }
+
+    mutating func decodeSingularFloatField(value: inout Float?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        value = try scanner.nextFloat()
+    }
+
+    mutating func decodeRepeatedFloatField(value: inout [Float]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextFloat()
+            value.append(n)
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularDoubleField(value: inout Double) throws {
+        if scanner.skipOptionalNull() {
+            value = 0
+            return
+        }
+        value = try scanner.nextDouble()
+    }
+
+    mutating func decodeSingularDoubleField(value: inout Double?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        value = try scanner.nextDouble()
+    }
+
+    mutating func decodeRepeatedDoubleField(value: inout [Double]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextDouble()
+            value.append(n)
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularInt32Field(value: inout Int32) throws {
+        if scanner.skipOptionalNull() {
+            value = 0
+            return
+        }
+        let n = try scanner.nextSInt()
+        if n > Int64(Int32.max) || n < Int64(Int32.min) {
+            throw JSONDecodingError.numberRange
+        }
+        value = Int32(truncatingIfNeeded: n)
+    }
+
+    mutating func decodeSingularInt32Field(value: inout Int32?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        let n = try scanner.nextSInt()
+        if n > Int64(Int32.max) || n < Int64(Int32.min) {
+            throw JSONDecodingError.numberRange
+        }
+        value = Int32(truncatingIfNeeded: n)
+    }
+
+    mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextSInt()
+            if n > Int64(Int32.max) || n < Int64(Int32.min) {
+                throw JSONDecodingError.numberRange
+            }
+            value.append(Int32(truncatingIfNeeded: n))
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularInt64Field(value: inout Int64) throws {
+        if scanner.skipOptionalNull() {
+            value = 0
+            return
+        }
+        value = try scanner.nextSInt()
+    }
+
+    mutating func decodeSingularInt64Field(value: inout Int64?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        value = try scanner.nextSInt()
+    }
+
+    mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextSInt()
+            value.append(n)
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularUInt32Field(value: inout UInt32) throws {
+        if scanner.skipOptionalNull() {
+            value = 0
+            return
+        }
+        let n = try scanner.nextUInt()
+        if n > UInt64(UInt32.max) {
+            throw JSONDecodingError.numberRange
+        }
+        value = UInt32(truncatingIfNeeded: n)
+    }
+
+    mutating func decodeSingularUInt32Field(value: inout UInt32?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        let n = try scanner.nextUInt()
+        if n > UInt64(UInt32.max) {
+            throw JSONDecodingError.numberRange
+        }
+        value = UInt32(truncatingIfNeeded: n)
+    }
+
+    mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextUInt()
+            if n > UInt64(UInt32.max) {
+                throw JSONDecodingError.numberRange
+            }
+            value.append(UInt32(truncatingIfNeeded: n))
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularUInt64Field(value: inout UInt64) throws {
+        if scanner.skipOptionalNull() {
+            value = 0
+            return
+        }
+        value = try scanner.nextUInt()
+    }
+
+    mutating func decodeSingularUInt64Field(value: inout UInt64?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        value = try scanner.nextUInt()
+    }
+
+    mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextUInt()
+            value.append(n)
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularSInt32Field(value: inout Int32) throws {
+        try decodeSingularInt32Field(value: &value)
+    }
+
+    mutating func decodeSingularSInt32Field(value: inout Int32?) throws {
+        try decodeSingularInt32Field(value: &value)
+    }
+
+    mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws {
+        try decodeRepeatedInt32Field(value: &value)
+    }
+
+    mutating func decodeSingularSInt64Field(value: inout Int64) throws {
+        try decodeSingularInt64Field(value: &value)
+    }
+
+    mutating func decodeSingularSInt64Field(value: inout Int64?) throws {
+        try decodeSingularInt64Field(value: &value)
+    }
+
+    mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws {
+        try decodeRepeatedInt64Field(value: &value)
+    }
+
+    mutating func decodeSingularFixed32Field(value: inout UInt32) throws {
+        try decodeSingularUInt32Field(value: &value)
+    }
+
+    mutating func decodeSingularFixed32Field(value: inout UInt32?) throws {
+        try decodeSingularUInt32Field(value: &value)
+    }
+
+    mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws {
+        try decodeRepeatedUInt32Field(value: &value)
+    }
+
+    mutating func decodeSingularFixed64Field(value: inout UInt64) throws {
+        try decodeSingularUInt64Field(value: &value)
+    }
+
+    mutating func decodeSingularFixed64Field(value: inout UInt64?) throws {
+        try decodeSingularUInt64Field(value: &value)
+    }
+
+    mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws {
+        try decodeRepeatedUInt64Field(value: &value)
+    }
+
+    mutating func decodeSingularSFixed32Field(value: inout Int32) throws {
+        try decodeSingularInt32Field(value: &value)
+    }
+
+    mutating func decodeSingularSFixed32Field(value: inout Int32?) throws {
+        try decodeSingularInt32Field(value: &value)
+    }
+
+    mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws {
+        try decodeRepeatedInt32Field(value: &value)
+    }
+
+    mutating func decodeSingularSFixed64Field(value: inout Int64) throws {
+        try decodeSingularInt64Field(value: &value)
+    }
+
+    mutating func decodeSingularSFixed64Field(value: inout Int64?) throws {
+        try decodeSingularInt64Field(value: &value)
+    }
+
+    mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws {
+        try decodeRepeatedInt64Field(value: &value)
+    }
+
+    mutating func decodeSingularBoolField(value: inout Bool) throws {
+        if scanner.skipOptionalNull() {
+            value = false
+            return
+        }
+        if isMapKey {
+            value = try scanner.nextQuotedBool()
         } else {
-          throw JSONDecodingError.illegalNull
+            value = try scanner.nextBool()
+        }
+    }
+
+    mutating func decodeSingularBoolField(value: inout Bool?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        if isMapKey {
+            value = try scanner.nextQuotedBool()
+        } else {
+            value = try scanner.nextBool()
+        }
+    }
+
+    mutating func decodeRepeatedBoolField(value: inout [Bool]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextBool()
+            value.append(n)
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularStringField(value: inout String) throws {
+        if scanner.skipOptionalNull() {
+            value = String()
+            return
+        }
+        value = try scanner.nextQuotedString()
+    }
+
+    mutating func decodeSingularStringField(value: inout String?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        value = try scanner.nextQuotedString()
+    }
+
+    mutating func decodeRepeatedStringField(value: inout [String]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextQuotedString()
+            value.append(n)
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularBytesField(value: inout Data) throws {
+        if scanner.skipOptionalNull() {
+            value = Data()
+            return
+        }
+        value = try scanner.nextBytesValue()
+    }
+
+    mutating func decodeSingularBytesField(value: inout Data?) throws {
+        if scanner.skipOptionalNull() {
+            value = nil
+            return
+        }
+        value = try scanner.nextBytesValue()
+    }
+
+    mutating func decodeRepeatedBytesField(value: inout [Data]) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            let n = try scanner.nextBytesValue()
+            value.append(n)
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularEnumField<E: Enum>(value: inout E?) throws
+    where E.RawValue == Int {
+        if scanner.skipOptionalNull() {
+            if let customDecodable = E.self as? any _CustomJSONCodable.Type {
+                value = try customDecodable.decodedFromJSONNull() as? E
+                return
+            }
+            value = nil
+            return
+        }
+        // Only change the value if a value was read.
+        if let e: E = try scanner.nextEnumValue() {
+            value = e
+        }
+    }
+
+    mutating func decodeSingularEnumField<E: Enum>(value: inout E) throws
+    where E.RawValue == Int {
+        if scanner.skipOptionalNull() {
+            if let customDecodable = E.self as? any _CustomJSONCodable.Type {
+                value = try customDecodable.decodedFromJSONNull() as! E
+                return
+            }
+            value = E()
+            return
         }
-      } else {
         if let e: E = try scanner.nextEnumValue() {
-          value.append(e)
-        }
-      }
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  internal mutating func decodeFullObject<M: Message>(message: inout M) throws {
-    guard let nameProviding = (M.self as? any _ProtoNameProviding.Type) else {
-      throw JSONDecodingError.missingFieldNames
-    }
-    fieldNameMap = nameProviding._protobuf_nameMap
-    if let m = message as? (any _CustomJSONCodable) {
-      var customCodable = m
-      try customCodable.decodeJSON(from: &self)
-      message = customCodable as! M
-    } else {
-      try scanner.skipRequiredObjectStart()
-      if scanner.skipOptionalObjectEnd() {
-        return
-      }
-      try message.decodeMessage(decoder: &self)
-    }
-  }
-
-  mutating func decodeSingularMessageField<M: Message>(value: inout M?) throws {
-    if scanner.skipOptionalNull() {
-      if M.self is any _CustomJSONCodable.Type {
-        value =
-        try (M.self as! any _CustomJSONCodable.Type).decodedFromJSONNull() as? M
-        return
-      }
-      // All other message field types treat 'null' as an unset
-      value = nil
-      return
-    }
-    if value == nil {
-      value = M()
-    }
-    var subDecoder = JSONDecoder(scanner: scanner, messageType: M.self)
-    try subDecoder.decodeFullObject(message: &value!)
-    assert(scanner.recursionBudget == subDecoder.scanner.recursionBudget)
-    scanner = subDecoder.scanner
-  }
-
-  mutating func decodeRepeatedMessageField<M: Message>(
-    value: inout [M]
-  ) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredArrayStart()
-    if scanner.skipOptionalArrayEnd() {
-      return
-    }
-    while true {
-      if scanner.skipOptionalNull() {
-        var appended = false
-        if M.self is any _CustomJSONCodable.Type {
-          if let message = try (M.self as! any _CustomJSONCodable.Type)
-            .decodedFromJSONNull() as? M {
-            value.append(message)
-            appended = true
-          }
-        }
-        if !appended {
-          throw JSONDecodingError.illegalNull
-        }
-      } else {
-        var message = M()
+            value = e
+        }
+
+    }
+
+    mutating func decodeRepeatedEnumField<E: Enum>(value: inout [E]) throws
+    where E.RawValue == Int {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        let maybeCustomDecodable = E.self as? any _CustomJSONCodable.Type
+        while true {
+            if scanner.skipOptionalNull() {
+                if let customDecodable = maybeCustomDecodable {
+                    let e = try customDecodable.decodedFromJSONNull() as! E
+                    value.append(e)
+                } else {
+                    throw JSONDecodingError.illegalNull
+                }
+            } else {
+                if let e: E = try scanner.nextEnumValue() {
+                    value.append(e)
+                }
+            }
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    internal mutating func decodeFullObject<M: Message>(message: inout M) throws {
+        guard let nameProviding = (M.self as? any _ProtoNameProviding.Type) else {
+            throw JSONDecodingError.missingFieldNames
+        }
+        fieldNameMap = nameProviding._protobuf_nameMap
+        if let m = message as? (any _CustomJSONCodable) {
+            var customCodable = m
+            try customCodable.decodeJSON(from: &self)
+            message = customCodable as! M
+        } else {
+            try scanner.skipRequiredObjectStart()
+            if scanner.skipOptionalObjectEnd() {
+                return
+            }
+            try message.decodeMessage(decoder: &self)
+        }
+    }
+
+    mutating func decodeSingularMessageField<M: Message>(value: inout M?) throws {
+        if scanner.skipOptionalNull() {
+            if M.self is any _CustomJSONCodable.Type {
+                value =
+                    try (M.self as! any _CustomJSONCodable.Type).decodedFromJSONNull() as? M
+                return
+            }
+            // All other message field types treat 'null' as an unset
+            value = nil
+            return
+        }
+        if value == nil {
+            value = M()
+        }
         var subDecoder = JSONDecoder(scanner: scanner, messageType: M.self)
-        try subDecoder.decodeFullObject(message: &message)
-        value.append(message)
+        try subDecoder.decodeFullObject(message: &value!)
         assert(scanner.recursionBudget == subDecoder.scanner.recursionBudget)
         scanner = subDecoder.scanner
-      }
-      if scanner.skipOptionalArrayEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeSingularGroupField<G: Message>(value: inout G?) throws {
-    try decodeSingularMessageField(value: &value)
-  }
-
-  mutating func decodeRepeatedGroupField<G: Message>(value: inout [G]) throws {
-    try decodeRepeatedMessageField(value: &value)
-  }
-
-  mutating func decodeMapField<KeyType, ValueType: MapValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: inout _ProtobufMap<KeyType, ValueType>.BaseType
-  ) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredObjectStart()
-    if scanner.skipOptionalObjectEnd() {
-      return
-    }
-    while true {
-      // Next character must be double quote, because
-      // map keys must always be quoted strings.
-      let c = try scanner.peekOneCharacter()
-      if c != "\"" {
-        throw JSONDecodingError.unquotedMapKey
-      }
-      isMapKey = true
-      var keyField: KeyType.BaseType?
-      try KeyType.decodeSingular(value: &keyField, from: &self)
-      isMapKey = false
-      try scanner.skipRequiredColon()
-      var valueField: ValueType.BaseType?
-      try ValueType.decodeSingular(value: &valueField, from: &self)
-      if let keyField = keyField, let valueField = valueField {
-        value[keyField] = valueField
-      } else {
-        throw JSONDecodingError.malformedMap
-      }
-      if scanner.skipOptionalObjectEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
-  ) throws where ValueType.RawValue == Int {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredObjectStart()
-    if scanner.skipOptionalObjectEnd() {
-      return
-    }
-    while true {
-      // Next character must be double quote, because
-      // map keys must always be quoted strings.
-      let c = try scanner.peekOneCharacter()
-      if c != "\"" {
-        throw JSONDecodingError.unquotedMapKey
-      }
-      isMapKey = true
-      var keyFieldOpt: KeyType.BaseType?
-      try KeyType.decodeSingular(value: &keyFieldOpt, from: &self)
-      guard let keyField = keyFieldOpt else {
-        throw JSONDecodingError.malformedMap
-      }
-      isMapKey = false
-      try scanner.skipRequiredColon()
-      var valueField: ValueType?
-      try decodeSingularEnumField(value: &valueField)
-      if let valueField = valueField {
-        value[keyField] = valueField
-      } else {
-        // Nothing, the only way ``decodeSingularEnumField(value:)`` leaves
-        // it as nil is if ignoreUnknownFields option is enabled which also
-        // means to ignore unknown enum values.
-      }
-      if scanner.skipOptionalObjectEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
-  ) throws {
-    if scanner.skipOptionalNull() {
-      return
-    }
-    try scanner.skipRequiredObjectStart()
-    if scanner.skipOptionalObjectEnd() {
-      return
-    }
-    while true {
-      // Next character must be double quote, because
-      // map keys must always be quoted strings.
-      let c = try scanner.peekOneCharacter()
-      if c != "\"" {
-        throw JSONDecodingError.unquotedMapKey
-      }
-      isMapKey = true
-      var keyField: KeyType.BaseType?
-      try KeyType.decodeSingular(value: &keyField, from: &self)
-      isMapKey = false
-      try scanner.skipRequiredColon()
-      var valueField: ValueType?
-      try decodeSingularMessageField(value: &valueField)
-      if let keyField = keyField, let valueField = valueField {
-        value[keyField] = valueField
-      } else {
-        throw JSONDecodingError.malformedMap
-      }
-      if scanner.skipOptionalObjectEnd() {
-        return
-      }
-      try scanner.skipRequiredComma()
-    }
-  }
-
-  mutating func decodeExtensionField(
-    values: inout ExtensionFieldValueSet,
-    messageType: any Message.Type,
-    fieldNumber: Int
-  ) throws {
-    // Force-unwrap: we can only get here if the extension exists.
-    let ext = scanner.extensions[messageType, fieldNumber]!
-
-    try values.modify(index: fieldNumber) { fieldValue in
-      if fieldValue != nil {
-        try fieldValue!.decodeExtensionField(decoder: &self)
-      } else {
-        fieldValue = try ext._protobuf_newField(decoder: &self)
-      }
-    }
-  }
+    }
+
+    mutating func decodeRepeatedMessageField<M: Message>(
+        value: inout [M]
+    ) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredArrayStart()
+        if scanner.skipOptionalArrayEnd() {
+            return
+        }
+        while true {
+            if scanner.skipOptionalNull() {
+                var appended = false
+                if M.self is any _CustomJSONCodable.Type {
+                    if let message = try (M.self as! any _CustomJSONCodable.Type)
+                        .decodedFromJSONNull() as? M
+                    {
+                        value.append(message)
+                        appended = true
+                    }
+                }
+                if !appended {
+                    throw JSONDecodingError.illegalNull
+                }
+            } else {
+                var message = M()
+                var subDecoder = JSONDecoder(scanner: scanner, messageType: M.self)
+                try subDecoder.decodeFullObject(message: &message)
+                value.append(message)
+                assert(scanner.recursionBudget == subDecoder.scanner.recursionBudget)
+                scanner = subDecoder.scanner
+            }
+            if scanner.skipOptionalArrayEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeSingularGroupField<G: Message>(value: inout G?) throws {
+        try decodeSingularMessageField(value: &value)
+    }
+
+    mutating func decodeRepeatedGroupField<G: Message>(value: inout [G]) throws {
+        try decodeRepeatedMessageField(value: &value)
+    }
+
+    mutating func decodeMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMap<KeyType, ValueType>.BaseType
+    ) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredObjectStart()
+        if scanner.skipOptionalObjectEnd() {
+            return
+        }
+        while true {
+            // Next character must be double quote, because
+            // map keys must always be quoted strings.
+            let c = try scanner.peekOneCharacter()
+            if c != "\"" {
+                throw JSONDecodingError.unquotedMapKey
+            }
+            isMapKey = true
+            var keyField: KeyType.BaseType?
+            try KeyType.decodeSingular(value: &keyField, from: &self)
+            isMapKey = false
+            try scanner.skipRequiredColon()
+            var valueField: ValueType.BaseType?
+            try ValueType.decodeSingular(value: &valueField, from: &self)
+            if let keyField = keyField, let valueField = valueField {
+                value[keyField] = valueField
+            } else {
+                throw JSONDecodingError.malformedMap
+            }
+            if scanner.skipOptionalObjectEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
+    ) throws where ValueType.RawValue == Int {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredObjectStart()
+        if scanner.skipOptionalObjectEnd() {
+            return
+        }
+        while true {
+            // Next character must be double quote, because
+            // map keys must always be quoted strings.
+            let c = try scanner.peekOneCharacter()
+            if c != "\"" {
+                throw JSONDecodingError.unquotedMapKey
+            }
+            isMapKey = true
+            var keyFieldOpt: KeyType.BaseType?
+            try KeyType.decodeSingular(value: &keyFieldOpt, from: &self)
+            guard let keyField = keyFieldOpt else {
+                throw JSONDecodingError.malformedMap
+            }
+            isMapKey = false
+            try scanner.skipRequiredColon()
+            var valueField: ValueType?
+            try decodeSingularEnumField(value: &valueField)
+            if let valueField = valueField {
+                value[keyField] = valueField
+            } else {
+                // Nothing, the only way ``decodeSingularEnumField(value:)`` leaves
+                // it as nil is if ignoreUnknownFields option is enabled which also
+                // means to ignore unknown enum values.
+            }
+            if scanner.skipOptionalObjectEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
+    ) throws {
+        if scanner.skipOptionalNull() {
+            return
+        }
+        try scanner.skipRequiredObjectStart()
+        if scanner.skipOptionalObjectEnd() {
+            return
+        }
+        while true {
+            // Next character must be double quote, because
+            // map keys must always be quoted strings.
+            let c = try scanner.peekOneCharacter()
+            if c != "\"" {
+                throw JSONDecodingError.unquotedMapKey
+            }
+            isMapKey = true
+            var keyField: KeyType.BaseType?
+            try KeyType.decodeSingular(value: &keyField, from: &self)
+            isMapKey = false
+            try scanner.skipRequiredColon()
+            var valueField: ValueType?
+            try decodeSingularMessageField(value: &valueField)
+            if let keyField = keyField, let valueField = valueField {
+                value[keyField] = valueField
+            } else {
+                throw JSONDecodingError.malformedMap
+            }
+            if scanner.skipOptionalObjectEnd() {
+                return
+            }
+            try scanner.skipRequiredComma()
+        }
+    }
+
+    mutating func decodeExtensionField(
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type,
+        fieldNumber: Int
+    ) throws {
+        // Force-unwrap: we can only get here if the extension exists.
+        let ext = scanner.extensions[messageType, fieldNumber]!
+
+        try values.modify(index: fieldNumber) { fieldValue in
+            if fieldValue != nil {
+                try fieldValue!.decodeExtensionField(decoder: &self)
+            } else {
+                fieldValue = try ext._protobuf_newField(decoder: &self)
+            }
+        }
+    }
 }

+ 12 - 12
Sources/SwiftProtobuf/JSONDecodingOptions.swift

@@ -14,18 +14,18 @@
 
 /// Options for JSONDecoding.
 public struct JSONDecodingOptions: Sendable {
-  /// The maximum nesting of message with messages.  The default is 100.
-  ///
-  /// To prevent corrupt or malicious messages from causing stack overflows,
-  /// this controls how deep messages can be nested within other messages
-  /// while parsing.
-  public var messageDepthLimit: Int = 100
+    /// The maximum nesting of message with messages.  The default is 100.
+    ///
+    /// To prevent corrupt or malicious messages from causing stack overflows,
+    /// this controls how deep messages can be nested within other messages
+    /// while parsing.
+    public var messageDepthLimit: Int = 100
 
-  /// If unknown fields in the JSON should be ignored. If they aren't
-  /// ignored, an error will be raised if one is encountered. This also
-  /// causes unknown enum values (especially string values) to be silently
-  /// ignored.
-  public var ignoreUnknownFields: Bool = false
+    /// If unknown fields in the JSON should be ignored. If they aren't
+    /// ignored, an error will be raised if one is encountered. This also
+    /// causes unknown enum values (especially string values) to be silently
+    /// ignored.
+    public var ignoreUnknownFields: Bool = false
 
-  public init() {}
+    public init() {}
 }

+ 35 - 36
Sources/SwiftProtobuf/JSONEncoder.swift

@@ -69,15 +69,15 @@ internal struct JSONEncoder {
 
     internal init() {}
 
-    internal var dataResult: [UInt8] { return data }
+    internal var dataResult: [UInt8] { data }
 
     internal var stringResult: String {
         get {
-            return String(bytes: data, encoding: String.Encoding.utf8)!
+            String(bytes: data, encoding: String.Encoding.utf8)!
         }
     }
 
-    internal var bytesResult: [UInt8] { return data }
+    internal var bytesResult: [UInt8] { data }
 
     /// Append a `StaticString` to the JSON text.  Because
     /// `StaticString` is already UTF8 internally, this is faster
@@ -328,7 +328,7 @@ internal struct JSONEncoder {
             case 13: append(staticText: "\\r")
             case 34: append(staticText: "\\\"")
             case 92: append(staticText: "\\\\")
-            case 0...31, 127...159: // Hex form for C0 control chars
+            case 0...31, 127...159:  // Hex form for C0 control chars
                 append(staticText: "\\u00")
                 data.append(hexDigits[Int(c.value / 16)])
                 data.append(hexDigits[Int(c.value & 15)])
@@ -356,46 +356,45 @@ internal struct JSONEncoder {
         data.append(asciiDoubleQuote)
         if value.count > 0 {
             value.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-              if let p = body.baseAddress, body.count > 0 {
-                var t: Int = 0
-                var bytesInGroup: Int = 0
-                for i in 0..<body.count {
-                    if bytesInGroup == 3 {
+                if let p = body.baseAddress, body.count > 0 {
+                    var t: Int = 0
+                    var bytesInGroup: Int = 0
+                    for i in 0..<body.count {
+                        if bytesInGroup == 3 {
+                            data.append(base64Digits[(t >> 18) & 63])
+                            data.append(base64Digits[(t >> 12) & 63])
+                            data.append(base64Digits[(t >> 6) & 63])
+                            data.append(base64Digits[t & 63])
+                            t = 0
+                            bytesInGroup = 0
+                        }
+                        t = (t << 8) + Int(p[i])
+                        bytesInGroup += 1
+                    }
+                    switch bytesInGroup {
+                    case 3:
                         data.append(base64Digits[(t >> 18) & 63])
                         data.append(base64Digits[(t >> 12) & 63])
                         data.append(base64Digits[(t >> 6) & 63])
                         data.append(base64Digits[t & 63])
-                        t = 0
-                        bytesInGroup = 0
+                    case 2:
+                        t <<= 8
+                        data.append(base64Digits[(t >> 18) & 63])
+                        data.append(base64Digits[(t >> 12) & 63])
+                        data.append(base64Digits[(t >> 6) & 63])
+                        data.append(asciiEquals)
+                    case 1:
+                        t <<= 16
+                        data.append(base64Digits[(t >> 18) & 63])
+                        data.append(base64Digits[(t >> 12) & 63])
+                        data.append(asciiEquals)
+                        data.append(asciiEquals)
+                    default:
+                        break
                     }
-                    t = (t << 8) + Int(p[i])
-                    bytesInGroup += 1
                 }
-                switch bytesInGroup {
-                case 3:
-                    data.append(base64Digits[(t >> 18) & 63])
-                    data.append(base64Digits[(t >> 12) & 63])
-                    data.append(base64Digits[(t >> 6) & 63])
-                    data.append(base64Digits[t & 63])
-                case 2:
-                    t <<= 8
-                    data.append(base64Digits[(t >> 18) & 63])
-                    data.append(base64Digits[(t >> 12) & 63])
-                    data.append(base64Digits[(t >> 6) & 63])
-                    data.append(asciiEquals)
-                case 1:
-                    t <<= 16
-                    data.append(base64Digits[(t >> 18) & 63])
-                    data.append(base64Digits[(t >> 12) & 63])
-                    data.append(asciiEquals)
-                    data.append(asciiEquals)
-                default:
-                    break
-                }
-              }
             }
         }
         data.append(asciiDoubleQuote)
     }
 }
-

+ 23 - 23
Sources/SwiftProtobuf/JSONEncodingOptions.swift

@@ -15,31 +15,31 @@
 /// Options for JSONEncoding.
 public struct JSONEncodingOptions: Sendable {
 
-  /// Always prints int64s values as numbers.
-  /// By default, they are printed as strings as per proto3 JSON mapping rules.
-  /// NB: When used as Map keys, int64s will be printed as strings as expected.
-  public var alwaysPrintInt64sAsNumbers: Bool = false
+    /// Always prints int64s values as numbers.
+    /// By default, they are printed as strings as per proto3 JSON mapping rules.
+    /// NB: When used as Map keys, int64s will be printed as strings as expected.
+    public var alwaysPrintInt64sAsNumbers: Bool = false
 
-  /// Always print enums as ints. By default they are printed as strings.
-  public var alwaysPrintEnumsAsInts: Bool = false
+    /// Always print enums as ints. By default they are printed as strings.
+    public var alwaysPrintEnumsAsInts: Bool = false
 
-  /// Whether to preserve proto field names.
-  /// By default they are converted to JSON(lowerCamelCase) names.
-  public var preserveProtoFieldNames: Bool = false
+    /// Whether to preserve proto field names.
+    /// By default they are converted to JSON(lowerCamelCase) names.
+    public var preserveProtoFieldNames: Bool = false
 
-  /// Whether to use deterministic ordering when serializing.
-  ///
-  /// Note that the deterministic serialization is NOT canonical across languages. 
-  /// It is NOT guaranteed to remain stable over time. It is unstable across 
-  /// different builds with schema changes due to unknown fields. Users who need
-  /// canonical serialization (e.g., persistent storage in a canonical form,
-  /// fingerprinting, etc.) should define their own canonicalization specification
-  /// and implement their own serializer rather than relying on this API.
-  ///
-  /// If deterministic serialization is requested, map entries will be sorted
-  /// by keys in lexicographical order. This is an implementation detail
-  /// and subject to change.
-  public var useDeterministicOrdering: Bool = false
+    /// Whether to use deterministic ordering when serializing.
+    ///
+    /// Note that the deterministic serialization is NOT canonical across languages.
+    /// It is NOT guaranteed to remain stable over time. It is unstable across
+    /// different builds with schema changes due to unknown fields. Users who need
+    /// canonical serialization (e.g., persistent storage in a canonical form,
+    /// fingerprinting, etc.) should define their own canonicalization specification
+    /// and implement their own serializer rather than relying on this API.
+    ///
+    /// If deterministic serialization is requested, map entries will be sorted
+    /// by keys in lexicographical order. This is an implementation detail
+    /// and subject to change.
+    public var useDeterministicOrdering: Bool = false
 
-  public init() {}
+    public init() {}
 }

+ 414 - 402
Sources/SwiftProtobuf/JSONEncodingVisitor.swift

@@ -17,411 +17,423 @@ import Foundation
 /// Visitor that serializes a message into JSON format.
 internal struct JSONEncodingVisitor: Visitor {
 
-  private var encoder = JSONEncoder()
-  private var nameMap: _NameMap
-  private var extensions: ExtensionFieldValueSet?
-  private let options: JSONEncodingOptions
-
-  /// The JSON text produced by the visitor, as raw UTF8 bytes.
-  var dataResult: [UInt8] {
-    return encoder.dataResult
-  }
-
-  /// The JSON text produced by the visitor, as a String.
-  internal var stringResult: String {
-      return encoder.stringResult
-  }
-
-  /// Creates a new visitor for serializing a message of the given type to JSON
-  /// format.
-  init(type: any Message.Type, options: JSONEncodingOptions) throws {
-    if let nameProviding = type as? any _ProtoNameProviding.Type {
-      self.nameMap = nameProviding._protobuf_nameMap
-    } else {
-      throw JSONEncodingError.missingFieldNames
-    }
-    self.options = options
-  }
-
-  mutating func startArray() {
-    encoder.startArray()
-  }
-
-  mutating func endArray() {
-    encoder.endArray()
-  }
-
-  mutating func startObject(message: any Message) {
-    self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
-    encoder.startObject()
-  }
-
-  mutating func startArrayObject(message: any Message) {
-    self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
-    encoder.startArrayObject()
-  }
-
-  mutating func endObject() {
-    encoder.endObject()
-  }
-
-  mutating func encodeField(name: String, stringValue value: String) {
-    encoder.startField(name: name)
-    encoder.putStringValue(value: value)
-  }
-
-  mutating func encodeField(name: String, jsonText text: String) {
-    encoder.startField(name: name)
-    encoder.append(text: text)
-  }
-
-  mutating func visitUnknown(bytes: Data) throws {
-    // JSON encoding has no provision for carrying proto2 unknown fields.
-  }
-
-  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putFloatValue(value: value)
-  }
-
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putDoubleValue(value: value)
-  }
-
-  mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putNonQuotedInt32(value: value)
-  }
-
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    options.alwaysPrintInt64sAsNumbers
-      ? encoder.putNonQuotedInt64(value: value)
-      : encoder.putQuotedInt64(value: value)
-  }
-
-  mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putNonQuotedUInt32(value: value)
-  }
-
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    options.alwaysPrintInt64sAsNumbers
-      ? encoder.putNonQuotedUInt64(value: value)
-      : encoder.putQuotedUInt64(value: value)
-  }
-
-  mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putNonQuotedUInt32(value: value)
-  }
-
-  mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putNonQuotedInt32(value: value)
-  }
-
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putNonQuotedBoolValue(value: value)
-  }
-
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putStringValue(value: value)
-  }
-
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    encoder.putBytesValue(value: value)
-  }
-
-  private mutating func _visitRepeated<T>(
-    value: [T],
-    fieldNumber: Int,
-    encode: (inout JSONEncoder, T) throws -> ()
-  ) throws {
-    assert(!value.isEmpty)
-    try startField(for: fieldNumber)
-    var comma = false
-    encoder.startArray()
-    for v in value {
-      if comma {
-          encoder.comma()
-      }
-      comma = true
-      try encode(&encoder, v)
-    }
-    encoder.endArray()
-  }
-
-  mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    if let e = value as? (any _CustomJSONCodable) {
-      let json = try e.encodedJSONString(options: options)
-      encoder.append(text: json)
-    } else if !options.alwaysPrintEnumsAsInts, let n = value.name {
-      encoder.appendQuoted(name: n)
-    } else {
-      encoder.putEnumInt(value: value.rawValue)
-    }
-  }
-
-  mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
-    try startField(for: fieldNumber)
-    if let m = value as? (any _CustomJSONCodable) {
-      let json = try m.encodedJSONString(options: options)
-      encoder.append(text: json)
-    } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap {
-      // Preserve outer object's name and extension maps; restore them before returning
-      let oldNameMap = self.nameMap
-      let oldExtensions = self.extensions
-      // Install inner object's name and extension maps
-      self.nameMap = newNameMap
-      startObject(message: value)
-      try value.traverse(visitor: &self)
-      endObject()
-      self.nameMap = oldNameMap
-      self.extensions = oldExtensions
-    } else {
-      throw JSONEncodingError.missingFieldNames
-    }
-  }
-
-  mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
-    try visitSingularMessageField(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
-    try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-      (encoder: inout JSONEncoder, v: Float) in
-      encoder.putFloatValue(value: v)
-    }
-  }
-
-  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
-    try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-      (encoder: inout JSONEncoder, v: Double) in
-      encoder.putDoubleValue(value: v)
-    }
-  }
-
-  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-      (encoder: inout JSONEncoder, v: Int32) in
-      encoder.putNonQuotedInt32(value: v)
-    }
-  }
-
-  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    if options.alwaysPrintInt64sAsNumbers {
-      try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-        (encoder: inout JSONEncoder, v: Int64) in
-        encoder.putNonQuotedInt64(value: v)
-      }
-    } else {
-      try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-        (encoder: inout JSONEncoder, v: Int64) in
-        encoder.putQuotedInt64(value: v)
-      }
-    }
-  }
-
-   mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-      (encoder: inout JSONEncoder, v: UInt32) in
-      encoder.putNonQuotedUInt32(value: v)
-    }
-  }
-
-  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    if options.alwaysPrintInt64sAsNumbers {
-      try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-        (encoder: inout JSONEncoder, v: UInt64) in
-        encoder.putNonQuotedUInt64(value: v)
-      }
-    } else {
-      try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-        (encoder: inout JSONEncoder, v: UInt64) in
-        encoder.putQuotedUInt64(value: v)
-      }
-    }
-  }
-
-   mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-   mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
-    try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-      (encoder: inout JSONEncoder, v: Bool) in
-      encoder.putNonQuotedBoolValue(value: v)
-    }
-  }
-
-  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
-    try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-      (encoder: inout JSONEncoder, v: String) in
-      encoder.putStringValue(value: v)
-    }
-  }
-
-  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
-    try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-      (encoder: inout JSONEncoder, v: Data) in
-      encoder.putBytesValue(value: v)
-    }
-  }
-
-  mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    if let _ = E.self as? any _CustomJSONCodable.Type {
-      let options = self.options
-      try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-        (encoder: inout JSONEncoder, v: E) throws in
-        let e = v as! (any _CustomJSONCodable)
-        let json = try e.encodedJSONString(options: options)
-        encoder.append(text: json)
-      }
-    } else {
-      let alwaysPrintEnumsAsInts = options.alwaysPrintEnumsAsInts
-      try _visitRepeated(value: value, fieldNumber: fieldNumber) {
-        (encoder: inout JSONEncoder, v: E) throws in
-        if !alwaysPrintEnumsAsInts, let n = v.name {
-          encoder.appendQuoted(name: n)
+    private var encoder = JSONEncoder()
+    private var nameMap: _NameMap
+    private var extensions: ExtensionFieldValueSet?
+    private let options: JSONEncodingOptions
+
+    /// The JSON text produced by the visitor, as raw UTF8 bytes.
+    var dataResult: [UInt8] {
+        encoder.dataResult
+    }
+
+    /// The JSON text produced by the visitor, as a String.
+    internal var stringResult: String {
+        encoder.stringResult
+    }
+
+    /// Creates a new visitor for serializing a message of the given type to JSON
+    /// format.
+    init(type: any Message.Type, options: JSONEncodingOptions) throws {
+        if let nameProviding = type as? any _ProtoNameProviding.Type {
+            self.nameMap = nameProviding._protobuf_nameMap
         } else {
-          encoder.putEnumInt(value: v.rawValue)
-        }
-      }
-    }
-  }
-
-  mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try startField(for: fieldNumber)
-    var comma = false
-    encoder.startArray()
-    if let _ = M.self as? any _CustomJSONCodable.Type {
-      for v in value {
-        if comma {
-          encoder.comma()
+            throw JSONEncodingError.missingFieldNames
         }
-        comma = true
-        let json = try v.jsonString(options: options)
-        encoder.append(text: json)
-      }
-    } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap {
-      // Preserve name and extension maps for outer object
-      let oldNameMap = self.nameMap
-      let oldExtensions = self.extensions
-      self.nameMap = newNameMap
-      for v in value {
-        startArrayObject(message: v)
-        try v.traverse(visitor: &self)
+        self.options = options
+    }
+
+    mutating func startArray() {
+        encoder.startArray()
+    }
+
+    mutating func endArray() {
+        encoder.endArray()
+    }
+
+    mutating func startObject(message: any Message) {
+        self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
+        encoder.startObject()
+    }
+
+    mutating func startArrayObject(message: any Message) {
+        self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
+        encoder.startArrayObject()
+    }
+
+    mutating func endObject() {
         encoder.endObject()
-      }
-      // Restore outer object's name and extension maps before returning
-      self.nameMap = oldNameMap
-      self.extensions = oldExtensions
-    } else {
-      throw JSONEncodingError.missingFieldNames
-    }
-    encoder.endArray()
-  }
-
-  mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
-    try visitRepeatedMessageField(value: value, fieldNumber: fieldNumber)
-  }
-
-  // Packed fields are handled the same as non-packed fields, so JSON just
-  // relies on the default implementations in Visitor.swift
-
-  mutating func visitMapField<KeyType, ValueType: MapValueType>(fieldType: _ProtobufMap<KeyType, ValueType>.Type, value: _ProtobufMap<KeyType, ValueType>.BaseType, fieldNumber: Int) throws {
-    try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
-      (visitor: inout JSONMapEncodingVisitor, key, value) throws -> () in
-      try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-      try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
-    }
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type, value: _ProtobufEnumMap<KeyType, ValueType>.BaseType, fieldNumber: Int) throws  where ValueType.RawValue == Int {
-    try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
-      (visitor: inout JSONMapEncodingVisitor, key, value) throws -> () in
-      try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-      try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
-    }
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type, value: _ProtobufMessageMap<KeyType, ValueType>.BaseType, fieldNumber: Int) throws {
-    try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
-      (visitor: inout JSONMapEncodingVisitor, key, value) throws -> () in
-      try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-      try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
-    }
-  }
-
-  /// Helper to encapsulate the common structure of iterating over a map
-  /// and encoding the keys and values.
-  private mutating func iterateAndEncode<K, V>(
-    map: Dictionary<K, V>,
-    fieldNumber: Int,
-    isOrderedBefore: (K, K) -> Bool,
-    encode: (inout JSONMapEncodingVisitor, K, V) throws -> ()
-  ) throws {
-    try startField(for: fieldNumber)
-    encoder.append(text: "{")
-    var mapVisitor = JSONMapEncodingVisitor(encoder: JSONEncoder(), options: options)
-    if options.useDeterministicOrdering {
-      for (k,v) in map.sorted(by: { isOrderedBefore( $0.0, $1.0) }) {
-        try encode(&mapVisitor, k, v)
-      }
-    } else {
-      for (k,v) in map {
-        try encode(&mapVisitor, k, v)
-      }
-    }
-    encoder.append(utf8Bytes: mapVisitor.bytesResult)
-    encoder.append(text: "}")
-  }
-
-  /// Helper function that throws an error if the field number could not be
-  /// resolved.
-  private mutating func startField(for number: Int) throws {
-    let name: _NameMap.Name?
-
-    if options.preserveProtoFieldNames {
-        name = nameMap.names(for: number)?.proto
-    } else {
-        name = nameMap.names(for: number)?.json
-    }
-
-    if let name = name {
+    }
+
+    mutating func encodeField(name: String, stringValue value: String) {
         encoder.startField(name: name)
-    } else if let name = extensions?[number]?.protobufExtension.fieldName {
-        encoder.startExtensionField(name: name)
-    } else {
-      throw JSONEncodingError.missingFieldNames
+        encoder.putStringValue(value: value)
+    }
+
+    mutating func encodeField(name: String, jsonText text: String) {
+        encoder.startField(name: name)
+        encoder.append(text: text)
+    }
+
+    mutating func visitUnknown(bytes: Data) throws {
+        // JSON encoding has no provision for carrying proto2 unknown fields.
+    }
+
+    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putFloatValue(value: value)
+    }
+
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putDoubleValue(value: value)
+    }
+
+    mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putNonQuotedInt32(value: value)
+    }
+
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        options.alwaysPrintInt64sAsNumbers
+            ? encoder.putNonQuotedInt64(value: value)
+            : encoder.putQuotedInt64(value: value)
+    }
+
+    mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putNonQuotedUInt32(value: value)
+    }
+
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        options.alwaysPrintInt64sAsNumbers
+            ? encoder.putNonQuotedUInt64(value: value)
+            : encoder.putQuotedUInt64(value: value)
+    }
+
+    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putNonQuotedUInt32(value: value)
+    }
+
+    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putNonQuotedInt32(value: value)
+    }
+
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putNonQuotedBoolValue(value: value)
+    }
+
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putStringValue(value: value)
+    }
+
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        encoder.putBytesValue(value: value)
+    }
+
+    private mutating func _visitRepeated<T>(
+        value: [T],
+        fieldNumber: Int,
+        encode: (inout JSONEncoder, T) throws -> Void
+    ) throws {
+        assert(!value.isEmpty)
+        try startField(for: fieldNumber)
+        var comma = false
+        encoder.startArray()
+        for v in value {
+            if comma {
+                encoder.comma()
+            }
+            comma = true
+            try encode(&encoder, v)
+        }
+        encoder.endArray()
+    }
+
+    mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        if let e = value as? (any _CustomJSONCodable) {
+            let json = try e.encodedJSONString(options: options)
+            encoder.append(text: json)
+        } else if !options.alwaysPrintEnumsAsInts, let n = value.name {
+            encoder.appendQuoted(name: n)
+        } else {
+            encoder.putEnumInt(value: value.rawValue)
+        }
+    }
+
+    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
+        try startField(for: fieldNumber)
+        if let m = value as? (any _CustomJSONCodable) {
+            let json = try m.encodedJSONString(options: options)
+            encoder.append(text: json)
+        } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap {
+            // Preserve outer object's name and extension maps; restore them before returning
+            let oldNameMap = self.nameMap
+            let oldExtensions = self.extensions
+            // Install inner object's name and extension maps
+            self.nameMap = newNameMap
+            startObject(message: value)
+            try value.traverse(visitor: &self)
+            endObject()
+            self.nameMap = oldNameMap
+            self.extensions = oldExtensions
+        } else {
+            throw JSONEncodingError.missingFieldNames
+        }
+    }
+
+    mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
+        try visitSingularMessageField(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+        try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+            (encoder: inout JSONEncoder, v: Float) in
+            encoder.putFloatValue(value: v)
+        }
+    }
+
+    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+        try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+            (encoder: inout JSONEncoder, v: Double) in
+            encoder.putDoubleValue(value: v)
+        }
+    }
+
+    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+            (encoder: inout JSONEncoder, v: Int32) in
+            encoder.putNonQuotedInt32(value: v)
+        }
+    }
+
+    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        if options.alwaysPrintInt64sAsNumbers {
+            try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+                (encoder: inout JSONEncoder, v: Int64) in
+                encoder.putNonQuotedInt64(value: v)
+            }
+        } else {
+            try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+                (encoder: inout JSONEncoder, v: Int64) in
+                encoder.putQuotedInt64(value: v)
+            }
+        }
+    }
+
+    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+            (encoder: inout JSONEncoder, v: UInt32) in
+            encoder.putNonQuotedUInt32(value: v)
+        }
+    }
+
+    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        if options.alwaysPrintInt64sAsNumbers {
+            try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+                (encoder: inout JSONEncoder, v: UInt64) in
+                encoder.putNonQuotedUInt64(value: v)
+            }
+        } else {
+            try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+                (encoder: inout JSONEncoder, v: UInt64) in
+                encoder.putQuotedUInt64(value: v)
+            }
+        }
+    }
+
+    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+        try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+            (encoder: inout JSONEncoder, v: Bool) in
+            encoder.putNonQuotedBoolValue(value: v)
+        }
+    }
+
+    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+        try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+            (encoder: inout JSONEncoder, v: String) in
+            encoder.putStringValue(value: v)
+        }
+    }
+
+    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+        try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+            (encoder: inout JSONEncoder, v: Data) in
+            encoder.putBytesValue(value: v)
+        }
+    }
+
+    mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        if let _ = E.self as? any _CustomJSONCodable.Type {
+            let options = self.options
+            try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+                (encoder: inout JSONEncoder, v: E) throws in
+                let e = v as! (any _CustomJSONCodable)
+                let json = try e.encodedJSONString(options: options)
+                encoder.append(text: json)
+            }
+        } else {
+            let alwaysPrintEnumsAsInts = options.alwaysPrintEnumsAsInts
+            try _visitRepeated(value: value, fieldNumber: fieldNumber) {
+                (encoder: inout JSONEncoder, v: E) throws in
+                if !alwaysPrintEnumsAsInts, let n = v.name {
+                    encoder.appendQuoted(name: n)
+                } else {
+                    encoder.putEnumInt(value: v.rawValue)
+                }
+            }
+        }
+    }
+
+    mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try startField(for: fieldNumber)
+        var comma = false
+        encoder.startArray()
+        if let _ = M.self as? any _CustomJSONCodable.Type {
+            for v in value {
+                if comma {
+                    encoder.comma()
+                }
+                comma = true
+                let json = try v.jsonString(options: options)
+                encoder.append(text: json)
+            }
+        } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap {
+            // Preserve name and extension maps for outer object
+            let oldNameMap = self.nameMap
+            let oldExtensions = self.extensions
+            self.nameMap = newNameMap
+            for v in value {
+                startArrayObject(message: v)
+                try v.traverse(visitor: &self)
+                encoder.endObject()
+            }
+            // Restore outer object's name and extension maps before returning
+            self.nameMap = oldNameMap
+            self.extensions = oldExtensions
+        } else {
+            throw JSONEncodingError.missingFieldNames
+        }
+        encoder.endArray()
+    }
+
+    mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
+        try visitRepeatedMessageField(value: value, fieldNumber: fieldNumber)
+    }
+
+    // Packed fields are handled the same as non-packed fields, so JSON just
+    // relies on the default implementations in Visitor.swift
+
+    mutating func visitMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
+            (visitor: inout JSONMapEncodingVisitor, key, value) throws -> Void in
+            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+            try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where ValueType.RawValue == Int {
+        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
+            (visitor: inout JSONMapEncodingVisitor, key, value) throws -> Void in
+            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+            try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
+            (visitor: inout JSONMapEncodingVisitor, key, value) throws -> Void in
+            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+            try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
+        }
+    }
+
+    /// Helper to encapsulate the common structure of iterating over a map
+    /// and encoding the keys and values.
+    private mutating func iterateAndEncode<K, V>(
+        map: [K: V],
+        fieldNumber: Int,
+        isOrderedBefore: (K, K) -> Bool,
+        encode: (inout JSONMapEncodingVisitor, K, V) throws -> Void
+    ) throws {
+        try startField(for: fieldNumber)
+        encoder.append(text: "{")
+        var mapVisitor = JSONMapEncodingVisitor(encoder: JSONEncoder(), options: options)
+        if options.useDeterministicOrdering {
+            for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) {
+                try encode(&mapVisitor, k, v)
+            }
+        } else {
+            for (k, v) in map {
+                try encode(&mapVisitor, k, v)
+            }
+        }
+        encoder.append(utf8Bytes: mapVisitor.bytesResult)
+        encoder.append(text: "}")
+    }
+
+    /// Helper function that throws an error if the field number could not be
+    /// resolved.
+    private mutating func startField(for number: Int) throws {
+        let name: _NameMap.Name?
+
+        if options.preserveProtoFieldNames {
+            name = nameMap.names(for: number)?.proto
+        } else {
+            name = nameMap.names(for: number)?.json
+        }
+
+        if let name = name {
+            encoder.startField(name: name)
+        } else if let name = extensions?[number]?.protobufExtension.fieldName {
+            encoder.startExtensionField(name: name)
+        } else {
+            throw JSONEncodingError.missingFieldNames
+        }
     }
-  }
 }

+ 161 - 161
Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift

@@ -21,165 +21,165 @@ import Foundation
 /// as `fieldNumber:1`, values should be identified as `fieldNumber:2`
 ///
 internal struct JSONMapEncodingVisitor: SelectiveVisitor {
-  private var separator: StaticString?
-  internal var encoder: JSONEncoder
-  private let options: JSONEncodingOptions
-
-  init(encoder: JSONEncoder, options: JSONEncodingOptions) {
-      self.encoder = encoder
-      self.options = options
-  }
-
-  internal var bytesResult: [UInt8] {
-      get {
-          return encoder.bytesResult
-      }
-  }
-
-  private mutating func startKey() {
-      if let s = separator {
-          encoder.append(staticText: s)
-      } else {
-          separator = ","
-      }
-  }
-
-  private mutating func startValue() {
-      encoder.append(staticText: ":")
-  }
-
-  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-      // Doubles/Floats can never be map keys, only values
-      assert(fieldNumber == 2)
-      startValue()
-      encoder.putFloatValue(value: value)
-  }
-
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-      // Doubles/Floats can never be map keys, only values
-      assert(fieldNumber == 2)
-      startValue()
-      encoder.putDoubleValue(value: value)
-  }
-
-  mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
-      if fieldNumber == 1 {
-          startKey()
-          encoder.putQuotedInt32(value: value)
-      } else {
-          startValue()
-          encoder.putNonQuotedInt32(value: value)
-      }
-  }
-
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-      if fieldNumber == 1 {
-          startKey()
-          encoder.putQuotedInt64(value: value)
-      } else {
-          startValue()
-          options.alwaysPrintInt64sAsNumbers
-            ? encoder.putNonQuotedInt64(value: value)
-            : encoder.putQuotedInt64(value: value)
-      }
-  }
-
-  mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
-      if fieldNumber == 1 {
-          startKey()
-          encoder.putQuotedUInt32(value: value)
-      } else {
-          startValue()
-          encoder.putNonQuotedUInt32(value: value)
-      }
-  }
-
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-      if fieldNumber == 1 {
-          startKey()
-          encoder.putQuotedUInt64(value: value)
-      } else {
-          startValue()
-          options.alwaysPrintInt64sAsNumbers
-            ? encoder.putNonQuotedUInt64(value: value)
-            : encoder.putQuotedUInt64(value: value)
-      }
-  }
-
-  mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
-      try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
-      try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
-      try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
-      try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
-      try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
-      try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-      if fieldNumber == 1 {
-          startKey()
-          encoder.putQuotedBoolValue(value: value)
-      } else {
-          startValue()
-          encoder.putNonQuotedBoolValue(value: value)
-      }
-  }
-
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-      if fieldNumber == 1 {
-          startKey()
-      } else {
-          startValue()
-      }
-      encoder.putStringValue(value: value)
-  }
-
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-      // Bytes can only be map values, never keys
-      assert(fieldNumber == 2)
-      startValue()
-      encoder.putBytesValue(value: value)
-  }
-
-  mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
-      // Enums can only be map values, never keys
-      assert(fieldNumber == 2)
-      startValue()
-      if !options.alwaysPrintEnumsAsInts, let n = value.name {
-          encoder.putStringValue(value: String(describing: n))
-      } else {
-          encoder.putEnumInt(value: value.rawValue)
-      }
-  }
-
-  mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
-      // Messages can only be map values, never keys
-      assert(fieldNumber == 2)
-      startValue()
-      let json = try value.jsonString(options: options)
-      encoder.append(text: json)
-  }
-
-  // SelectiveVisitor will block:
-  // - single Groups
-  // - everything repeated
-  // - everything packed
-  // - all maps
-  // - unknown fields
-  // - extensions
+    private var separator: StaticString?
+    internal var encoder: JSONEncoder
+    private let options: JSONEncodingOptions
+
+    init(encoder: JSONEncoder, options: JSONEncodingOptions) {
+        self.encoder = encoder
+        self.options = options
+    }
+
+    internal var bytesResult: [UInt8] {
+        get {
+            encoder.bytesResult
+        }
+    }
+
+    private mutating func startKey() {
+        if let s = separator {
+            encoder.append(staticText: s)
+        } else {
+            separator = ","
+        }
+    }
+
+    private mutating func startValue() {
+        encoder.append(staticText: ":")
+    }
+
+    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        // Doubles/Floats can never be map keys, only values
+        assert(fieldNumber == 2)
+        startValue()
+        encoder.putFloatValue(value: value)
+    }
+
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        // Doubles/Floats can never be map keys, only values
+        assert(fieldNumber == 2)
+        startValue()
+        encoder.putDoubleValue(value: value)
+    }
+
+    mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
+        if fieldNumber == 1 {
+            startKey()
+            encoder.putQuotedInt32(value: value)
+        } else {
+            startValue()
+            encoder.putNonQuotedInt32(value: value)
+        }
+    }
+
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        if fieldNumber == 1 {
+            startKey()
+            encoder.putQuotedInt64(value: value)
+        } else {
+            startValue()
+            options.alwaysPrintInt64sAsNumbers
+                ? encoder.putNonQuotedInt64(value: value)
+                : encoder.putQuotedInt64(value: value)
+        }
+    }
+
+    mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
+        if fieldNumber == 1 {
+            startKey()
+            encoder.putQuotedUInt32(value: value)
+        } else {
+            startValue()
+            encoder.putNonQuotedUInt32(value: value)
+        }
+    }
+
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        if fieldNumber == 1 {
+            startKey()
+            encoder.putQuotedUInt64(value: value)
+        } else {
+            startValue()
+            options.alwaysPrintInt64sAsNumbers
+                ? encoder.putNonQuotedUInt64(value: value)
+                : encoder.putQuotedUInt64(value: value)
+        }
+    }
+
+    mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
+        try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+        try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
+        try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
+        try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        if fieldNumber == 1 {
+            startKey()
+            encoder.putQuotedBoolValue(value: value)
+        } else {
+            startValue()
+            encoder.putNonQuotedBoolValue(value: value)
+        }
+    }
+
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+        if fieldNumber == 1 {
+            startKey()
+        } else {
+            startValue()
+        }
+        encoder.putStringValue(value: value)
+    }
+
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+        // Bytes can only be map values, never keys
+        assert(fieldNumber == 2)
+        startValue()
+        encoder.putBytesValue(value: value)
+    }
+
+    mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
+        // Enums can only be map values, never keys
+        assert(fieldNumber == 2)
+        startValue()
+        if !options.alwaysPrintEnumsAsInts, let n = value.name {
+            encoder.putStringValue(value: String(describing: n))
+        } else {
+            encoder.putEnumInt(value: value.rawValue)
+        }
+    }
+
+    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
+        // Messages can only be map values, never keys
+        assert(fieldNumber == 2)
+        startValue()
+        let json = try value.jsonString(options: options)
+        encoder.append(text: json)
+    }
+
+    // SelectiveVisitor will block:
+    // - single Groups
+    // - everything repeated
+    // - everything packed
+    // - all maps
+    // - unknown fields
+    // - extensions
 }

+ 1261 - 1230
Sources/SwiftProtobuf/JSONScanner.swift

@@ -64,20 +64,20 @@ private let asciiLowerZ = UInt8(ascii: "z")
 private let asciiUpperZ = UInt8(ascii: "Z")
 
 private func fromHexDigit(_ c: UnicodeScalar) -> UInt32? {
-  let n = c.value
-  if n >= 48 && n <= 57 {
-    return UInt32(n - 48)
-  }
-  switch n {
-  case 65, 97: return 10
-  case 66, 98: return 11
-  case 67, 99: return 12
-  case 68, 100: return 13
-  case 69, 101: return 14
-  case 70, 102: return 15
-  default:
-    return nil
-  }
+    let n = c.value
+    if n >= 48 && n <= 57 {
+        return UInt32(n - 48)
+    }
+    switch n {
+    case 65, 97: return 10
+    case 66, 98: return 11
+    case 67, 99: return 12
+    case 68, 100: return 13
+    case 69, 101: return 14
+    case 70, 102: return 15
+    default:
+        return nil
+    }
 }
 
 // Decode both the RFC 4648 section 4 Base 64 encoding and the RFC
@@ -86,22 +86,22 @@ private func fromHexDigit(_ c: UnicodeScalar) -> UInt32? {
 // Note that both "-" and "+" decode to 62 and "/" and "_" both
 // decode as 63.
 let base64Values: [Int] = [
-/* 0x00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0x10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0x20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63,
-/* 0x30 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-/* 0x40 */ -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-/* 0x50 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
-/* 0x60 */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-/* 0x70 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-/* 0x80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0x90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0xa0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0xb0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0xc0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0xd0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0xe0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-/* 0xf0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0x00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0x10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0x20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63,
+    /* 0x30 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+    /* 0x40 */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+    /* 0x50 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
+    /* 0x60 */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    /* 0x70 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+    /* 0x80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0x90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0xa0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0xb0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0xc0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0xd0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0xe0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 0xf0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 ]
 
 /// Returns a `Data` value containing bytes equivalent to the given
@@ -116,9 +116,9 @@ let base64Values: [Int] = [
 ///    Base 64 encoding and the "URL and Filename Safe Alphabet" variant.
 ///
 private func parseBytes(
-  source: UnsafeRawBufferPointer,
-  index: inout UnsafeRawBufferPointer.Index,
-  end: UnsafeRawBufferPointer.Index
+    source: UnsafeRawBufferPointer,
+    index: inout UnsafeRawBufferPointer.Index,
+    end: UnsafeRawBufferPointer.Index
 ) throws -> Data {
     let c = source[index]
     if c != asciiDoubleQuote {
@@ -191,83 +191,83 @@ private func parseBytes(
     index = digitsStart
     try value.withUnsafeMutableBytes {
         (body: UnsafeMutableRawBufferPointer) in
-      if var p = body.baseAddress, body.count > 0 {
-        var n = 0
-        var chars = 0 // # chars in current group
-        var padding = 0 // # padding '=' chars
-        digits: while true {
-            let digit = source[index]
-            var k = base64Values[Int(digit)]
-            if k < 0 {
-                switch digit {
-                case asciiDoubleQuote:
-                    break digits
-                case asciiBackslash:
-                    source.formIndex(after: &index)
-                    let escaped = source[index]
-                    switch escaped {
-                    case asciiForwardSlash:
-                        k = base64Values[Int(escaped)]
-                    default:
-                        // Note: Invalid backslash escapes were caught
-                        // above; we should never get here.
-                        throw JSONDecodingError.malformedString
-                    }
-                case asciiSpace:
-                    source.formIndex(after: &index)
-                    continue digits
-                case asciiEqualSign: // Count padding
-                    while true {
-                        switch source[index] {
-                        case asciiDoubleQuote:
-                            break digits
-                        case asciiSpace:
-                            break
-                        case 61:
-                            padding += 1
-                        default: // Only '=' and whitespace permitted
+        if var p = body.baseAddress, body.count > 0 {
+            var n = 0
+            var chars = 0  // # chars in current group
+            var padding = 0  // # padding '=' chars
+            digits: while true {
+                let digit = source[index]
+                var k = base64Values[Int(digit)]
+                if k < 0 {
+                    switch digit {
+                    case asciiDoubleQuote:
+                        break digits
+                    case asciiBackslash:
+                        source.formIndex(after: &index)
+                        let escaped = source[index]
+                        switch escaped {
+                        case asciiForwardSlash:
+                            k = base64Values[Int(escaped)]
+                        default:
+                            // Note: Invalid backslash escapes were caught
+                            // above; we should never get here.
                             throw JSONDecodingError.malformedString
                         }
+                    case asciiSpace:
                         source.formIndex(after: &index)
+                        continue digits
+                    case asciiEqualSign:  // Count padding
+                        while true {
+                            switch source[index] {
+                            case asciiDoubleQuote:
+                                break digits
+                            case asciiSpace:
+                                break
+                            case 61:
+                                padding += 1
+                            default:  // Only '=' and whitespace permitted
+                                throw JSONDecodingError.malformedString
+                            }
+                            source.formIndex(after: &index)
+                        }
+                    default:
+                        throw JSONDecodingError.malformedString
                     }
-                default:
-                    throw JSONDecodingError.malformedString
                 }
+                n <<= 6
+                n |= k
+                chars += 1
+                if chars == 4 {
+                    p[0] = UInt8(truncatingIfNeeded: n >> 16)
+                    p[1] = UInt8(truncatingIfNeeded: n >> 8)
+                    p[2] = UInt8(truncatingIfNeeded: n)
+                    p += 3
+                    chars = 0
+                    n = 0
+                }
+                source.formIndex(after: &index)
             }
-            n <<= 6
-            n |= k
-            chars += 1
-            if chars == 4 {
-                p[0] = UInt8(truncatingIfNeeded: n >> 16)
-                p[1] = UInt8(truncatingIfNeeded: n >> 8)
-                p[2] = UInt8(truncatingIfNeeded: n)
-                p += 3
-                chars = 0
-                n = 0
-            }
-            source.formIndex(after: &index)
-        }
-        switch chars {
-        case 3:
-            p[0] = UInt8(truncatingIfNeeded: n >> 10)
-            p[1] = UInt8(truncatingIfNeeded: n >> 2)
-            if padding == 1 || padding == 0 {
-                return
-            }
-        case 2:
-            p[0] = UInt8(truncatingIfNeeded: n >> 4)
-            if padding == 2 || padding == 0 {
-                return
-            }
-        case 0:
-            if padding == 0 {
-                return
+            switch chars {
+            case 3:
+                p[0] = UInt8(truncatingIfNeeded: n >> 10)
+                p[1] = UInt8(truncatingIfNeeded: n >> 2)
+                if padding == 1 || padding == 0 {
+                    return
+                }
+            case 2:
+                p[0] = UInt8(truncatingIfNeeded: n >> 4)
+                if padding == 2 || padding == 0 {
+                    return
+                }
+            case 0:
+                if padding == 0 {
+                    return
+                }
+            default:
+                break
             }
-        default:
-            break
+            throw JSONDecodingError.malformedString
         }
-        throw JSONDecodingError.malformedString
-      }
     }
     source.formIndex(after: &index)
     return value
@@ -276,93 +276,95 @@ private func parseBytes(
 // JSON encoding allows a variety of \-escapes, including
 // escaping UTF-16 code points (which may be surrogate pairs).
 private func decodeString(_ s: String) -> String? {
-  var out = String.UnicodeScalarView()
-  var chars = s.unicodeScalars.makeIterator()
-  while let c = chars.next() {
-    switch c.value {
-    case UInt32(asciiBackslash): // backslash
-      if let escaped = chars.next() {
-        switch escaped.value {
-        case UInt32(asciiLowerU): // "\u"
-          // Exactly 4 hex digits:
-          if let digit1 = chars.next(),
-            let d1 = fromHexDigit(digit1),
-            let digit2 = chars.next(),
-            let d2 = fromHexDigit(digit2),
-            let digit3 = chars.next(),
-            let d3 = fromHexDigit(digit3),
-            let digit4 = chars.next(),
-            let d4 = fromHexDigit(digit4) {
-            let codePoint = ((d1 * 16 + d2) * 16 + d3) * 16 + d4
-            if let scalar = UnicodeScalar(codePoint) {
-              out.append(scalar)
-            } else if codePoint < 0xD800 || codePoint >= 0xE000 {
-              // Not a valid Unicode scalar.
-              return nil
-            } else if codePoint >= 0xDC00 {
-              // Low surrogate without a preceding high surrogate.
-              return nil
-            } else {
-              // We have a high surrogate (in the range 0xD800..<0xDC00), so
-              // verify that it is followed by a low surrogate.
-              guard chars.next() == "\\", chars.next() == "u" else {
-                // High surrogate was not followed by a Unicode escape sequence.
-                return nil
-              }
-              if let digit1 = chars.next(),
-                let d1 = fromHexDigit(digit1),
-                let digit2 = chars.next(),
-                let d2 = fromHexDigit(digit2),
-                let digit3 = chars.next(),
-                let d3 = fromHexDigit(digit3),
-                let digit4 = chars.next(),
-                let d4 = fromHexDigit(digit4) {
-                let follower = ((d1 * 16 + d2) * 16 + d3) * 16 + d4
-                guard 0xDC00 <= follower && follower < 0xE000 else {
-                  // High surrogate was not followed by a low surrogate.
-                  return nil
-                }
-                let high = codePoint - 0xD800
-                let low = follower - 0xDC00
-                let composed = 0x10000 | high << 10 | low
-                guard let composedScalar = UnicodeScalar(composed) else {
-                  // Composed value is not a valid Unicode scalar.
-                  return nil
+    var out = String.UnicodeScalarView()
+    var chars = s.unicodeScalars.makeIterator()
+    while let c = chars.next() {
+        switch c.value {
+        case UInt32(asciiBackslash):  // backslash
+            if let escaped = chars.next() {
+                switch escaped.value {
+                case UInt32(asciiLowerU):  // "\u"
+                    // Exactly 4 hex digits:
+                    if let digit1 = chars.next(),
+                        let d1 = fromHexDigit(digit1),
+                        let digit2 = chars.next(),
+                        let d2 = fromHexDigit(digit2),
+                        let digit3 = chars.next(),
+                        let d3 = fromHexDigit(digit3),
+                        let digit4 = chars.next(),
+                        let d4 = fromHexDigit(digit4)
+                    {
+                        let codePoint = ((d1 * 16 + d2) * 16 + d3) * 16 + d4
+                        if let scalar = UnicodeScalar(codePoint) {
+                            out.append(scalar)
+                        } else if codePoint < 0xD800 || codePoint >= 0xE000 {
+                            // Not a valid Unicode scalar.
+                            return nil
+                        } else if codePoint >= 0xDC00 {
+                            // Low surrogate without a preceding high surrogate.
+                            return nil
+                        } else {
+                            // We have a high surrogate (in the range 0xD800..<0xDC00), so
+                            // verify that it is followed by a low surrogate.
+                            guard chars.next() == "\\", chars.next() == "u" else {
+                                // High surrogate was not followed by a Unicode escape sequence.
+                                return nil
+                            }
+                            if let digit1 = chars.next(),
+                                let d1 = fromHexDigit(digit1),
+                                let digit2 = chars.next(),
+                                let d2 = fromHexDigit(digit2),
+                                let digit3 = chars.next(),
+                                let d3 = fromHexDigit(digit3),
+                                let digit4 = chars.next(),
+                                let d4 = fromHexDigit(digit4)
+                            {
+                                let follower = ((d1 * 16 + d2) * 16 + d3) * 16 + d4
+                                guard 0xDC00 <= follower && follower < 0xE000 else {
+                                    // High surrogate was not followed by a low surrogate.
+                                    return nil
+                                }
+                                let high = codePoint - 0xD800
+                                let low = follower - 0xDC00
+                                let composed = 0x10000 | high << 10 | low
+                                guard let composedScalar = UnicodeScalar(composed) else {
+                                    // Composed value is not a valid Unicode scalar.
+                                    return nil
+                                }
+                                out.append(composedScalar)
+                            } else {
+                                // Malformed \u escape for low surrogate
+                                return nil
+                            }
+                        }
+                    } else {
+                        // Malformed \u escape
+                        return nil
+                    }
+                case UInt32(asciiLowerB):  // \b
+                    out.append("\u{08}")
+                case UInt32(asciiLowerF):  // \f
+                    out.append("\u{0c}")
+                case UInt32(asciiLowerN):  // \n
+                    out.append("\u{0a}")
+                case UInt32(asciiLowerR):  // \r
+                    out.append("\u{0d}")
+                case UInt32(asciiLowerT):  // \t
+                    out.append("\u{09}")
+                case UInt32(asciiDoubleQuote), UInt32(asciiBackslash),
+                    UInt32(asciiForwardSlash):  // " \ /
+                    out.append(escaped)
+                default:
+                    return nil  // Unrecognized escape
                 }
-                out.append(composedScalar)
-              } else {
-                // Malformed \u escape for low surrogate
-                return nil
-              }
+            } else {
+                return nil  // Input ends with backslash
             }
-          } else {
-            // Malformed \u escape
-            return nil
-          }
-        case UInt32(asciiLowerB): // \b
-          out.append("\u{08}")
-        case UInt32(asciiLowerF): // \f
-          out.append("\u{0c}")
-        case UInt32(asciiLowerN): // \n
-          out.append("\u{0a}")
-        case UInt32(asciiLowerR): // \r
-          out.append("\u{0d}")
-        case UInt32(asciiLowerT): // \t
-          out.append("\u{09}")
-        case UInt32(asciiDoubleQuote), UInt32(asciiBackslash),
-             UInt32(asciiForwardSlash): // " \ /
-          out.append(escaped)
         default:
-          return nil // Unrecognized escape
+            out.append(c)
         }
-      } else {
-        return nil // Input ends with backslash
-      }
-    default:
-      out.append(c)
     }
-  }
-  return String(out)
+    return String(out)
 }
 
 ///
@@ -370,1174 +372,1203 @@ private func decodeString(_ s: String) -> String? {
 ///
 /// For performance, it works directly against UTF-8 bytes in memory.
 internal struct JSONScanner {
-  private let source: UnsafeRawBufferPointer
-  private var index: UnsafeRawBufferPointer.Index
-  private var numberParser = DoubleParser()
-  internal let options: JSONDecodingOptions
-  internal let extensions: any ExtensionMap
-  internal var recursionBudget: Int
+    private let source: UnsafeRawBufferPointer
+    private var index: UnsafeRawBufferPointer.Index
+    private var numberParser = DoubleParser()
+    internal let options: JSONDecodingOptions
+    internal let extensions: any ExtensionMap
+    internal var recursionBudget: Int
 
-  /// True if the scanner has read all of the data from the source, with the
-  /// exception of any trailing whitespace (which is consumed by reading this
-  /// property).
-  internal var complete: Bool {
-    mutating get {
-      skipWhitespace()
-      return !hasMoreContent
+    /// True if the scanner has read all of the data from the source, with the
+    /// exception of any trailing whitespace (which is consumed by reading this
+    /// property).
+    internal var complete: Bool {
+        mutating get {
+            skipWhitespace()
+            return !hasMoreContent
+        }
     }
-  }
-
-  /// True if the scanner has not yet reached the end of the source.
-  private var hasMoreContent: Bool {
-    return index != source.endIndex
-  }
 
-  /// The byte (UTF-8 code unit) at the scanner's current position.
-  private var currentByte: UInt8 {
-    return source[index]
-  }
+    /// True if the scanner has not yet reached the end of the source.
+    private var hasMoreContent: Bool {
+        index != source.endIndex
+    }
 
-  internal init(
-    source: UnsafeRawBufferPointer,
-    options: JSONDecodingOptions,
-    extensions: (any ExtensionMap)?
-  ) {
-    self.source = source
-    self.index = source.startIndex
-    self.recursionBudget = options.messageDepthLimit
-    self.options = options
-    self.extensions = extensions ?? SimpleExtensionMap()
-  }
+    /// The byte (UTF-8 code unit) at the scanner's current position.
+    private var currentByte: UInt8 {
+        source[index]
+    }
 
-  internal mutating func incrementRecursionDepth() throws {
-    recursionBudget -= 1
-    if recursionBudget < 0 {
-      throw JSONDecodingError.messageDepthLimit
+    internal init(
+        source: UnsafeRawBufferPointer,
+        options: JSONDecodingOptions,
+        extensions: (any ExtensionMap)?
+    ) {
+        self.source = source
+        self.index = source.startIndex
+        self.recursionBudget = options.messageDepthLimit
+        self.options = options
+        self.extensions = extensions ?? SimpleExtensionMap()
     }
-  }
 
-  internal mutating func decrementRecursionDepth() {
-    recursionBudget += 1
-    // This should never happen, if it does, something is probably corrupting memory, and
-    // simply throwing doesn't make much sense.
-    if recursionBudget > options.messageDepthLimit {
-      fatalError("Somehow JSONDecoding unwound more objects than it started")
+    internal mutating func incrementRecursionDepth() throws {
+        recursionBudget -= 1
+        if recursionBudget < 0 {
+            throw JSONDecodingError.messageDepthLimit
+        }
     }
-  }
 
-  /// Advances the scanner to the next position in the source.
-  private mutating func advance() {
-    source.formIndex(after: &index)
-  }
+    internal mutating func decrementRecursionDepth() {
+        recursionBudget += 1
+        // This should never happen, if it does, something is probably corrupting memory, and
+        // simply throwing doesn't make much sense.
+        if recursionBudget > options.messageDepthLimit {
+            fatalError("Somehow JSONDecoding unwound more objects than it started")
+        }
+    }
 
-  /// Skip whitespace.
-  private mutating func skipWhitespace() {
-    while hasMoreContent {
-      let u = currentByte
-      switch u {
-      case asciiSpace, asciiTab, asciiNewLine, asciiCarriageReturn:
-        advance()
-      default:
-        return
-      }
+    /// Advances the scanner to the next position in the source.
+    private mutating func advance() {
+        source.formIndex(after: &index)
     }
-  }
 
-  /// Returns (but does not consume) the next non-whitespace
-  /// character.  This is used by google.protobuf.Value, for
-  /// example, for custom JSON parsing.
-  internal mutating func peekOneCharacter() throws -> Character {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
+    /// Skip whitespace.
+    private mutating func skipWhitespace() {
+        while hasMoreContent {
+            let u = currentByte
+            switch u {
+            case asciiSpace, asciiTab, asciiNewLine, asciiCarriageReturn:
+                advance()
+            default:
+                return
+            }
+        }
     }
-    return Character(UnicodeScalar(UInt32(currentByte))!)
-  }
 
-  // Parse the leading UInt64 from the provided utf8 bytes.
-  //
-  // This is called in three different situations:
-  //
-  // * Unquoted number.
-  //
-  // * Simple quoted number.  If a number is quoted but has no
-  //   backslashes, the caller can use this directly on the UTF8 by
-  //   just verifying the quote marks.  This code returns `nil` if it
-  //   sees a backslash, in which case the caller will need to handle ...
-  //
-  // * Complex quoted number.  In this case, the caller must parse the
-  //   quoted value as a string, then convert the string to utf8 and
-  //   use this to parse the result.  This is slow but fortunately
-  //   rare.
-  //
-  // In the common case where the number is written in integer form,
-  // this code does a simple straight conversion.  If the number is in
-  // floating-point format, this uses a slower and less accurate
-  // approach: it identifies a substring comprising a float, and then
-  // uses Double() and UInt64() to convert that string to an unsigned
-  // integer.  In particular, it cannot preserve full 64-bit integer
-  // values when they are written in floating-point format.
-  //
-  // If it encounters a "\" backslash character, it returns a nil.  This
-  // is used by callers that are parsing quoted numbers.  See nextSInt()
-  // and nextUInt() below.
-  private func parseBareUInt64(
-    source: UnsafeRawBufferPointer,
-    index: inout UnsafeRawBufferPointer.Index,
-    end: UnsafeRawBufferPointer.Index
-  ) throws -> UInt64? {
-    if index == end {
-      throw JSONDecodingError.truncated
+    /// Returns (but does not consume) the next non-whitespace
+    /// character.  This is used by google.protobuf.Value, for
+    /// example, for custom JSON parsing.
+    internal mutating func peekOneCharacter() throws -> Character {
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
+        }
+        return Character(UnicodeScalar(UInt32(currentByte))!)
     }
-    let start = index
-    let c = source[index]
-    switch c {
-    case asciiZero: // 0
-      source.formIndex(after: &index)
-      if index != end {
-        let after = source[index]
-        switch after {
-        case asciiZero...asciiNine: // 0...9
-          // leading '0' forbidden unless it is the only digit
-          throw JSONDecodingError.leadingZero
-        case asciiPeriod, asciiLowerE, asciiUpperE: // . e
-          // Slow path: JSON numbers can be written in floating-point notation
-          index = start
-          if let d = try parseBareDouble(source: source,
-                                         index: &index,
-                                         end: end) {
-            if let u = UInt64(exactly: d) {
-              return u
+
+    // Parse the leading UInt64 from the provided utf8 bytes.
+    //
+    // This is called in three different situations:
+    //
+    // * Unquoted number.
+    //
+    // * Simple quoted number.  If a number is quoted but has no
+    //   backslashes, the caller can use this directly on the UTF8 by
+    //   just verifying the quote marks.  This code returns `nil` if it
+    //   sees a backslash, in which case the caller will need to handle ...
+    //
+    // * Complex quoted number.  In this case, the caller must parse the
+    //   quoted value as a string, then convert the string to utf8 and
+    //   use this to parse the result.  This is slow but fortunately
+    //   rare.
+    //
+    // In the common case where the number is written in integer form,
+    // this code does a simple straight conversion.  If the number is in
+    // floating-point format, this uses a slower and less accurate
+    // approach: it identifies a substring comprising a float, and then
+    // uses Double() and UInt64() to convert that string to an unsigned
+    // integer.  In particular, it cannot preserve full 64-bit integer
+    // values when they are written in floating-point format.
+    //
+    // If it encounters a "\" backslash character, it returns a nil.  This
+    // is used by callers that are parsing quoted numbers.  See nextSInt()
+    // and nextUInt() below.
+    private func parseBareUInt64(
+        source: UnsafeRawBufferPointer,
+        index: inout UnsafeRawBufferPointer.Index,
+        end: UnsafeRawBufferPointer.Index
+    ) throws -> UInt64? {
+        if index == end {
+            throw JSONDecodingError.truncated
+        }
+        let start = index
+        let c = source[index]
+        switch c {
+        case asciiZero:  // 0
+            source.formIndex(after: &index)
+            if index != end {
+                let after = source[index]
+                switch after {
+                case asciiZero...asciiNine:  // 0...9
+                    // leading '0' forbidden unless it is the only digit
+                    throw JSONDecodingError.leadingZero
+                case asciiPeriod, asciiLowerE, asciiUpperE:  // . e
+                    // Slow path: JSON numbers can be written in floating-point notation
+                    index = start
+                    if let d = try parseBareDouble(
+                        source: source,
+                        index: &index,
+                        end: end
+                    ) {
+                        if let u = UInt64(exactly: d) {
+                            return u
+                        }
+                    }
+                    throw JSONDecodingError.malformedNumber
+                case asciiBackslash:
+                    return nil
+                default:
+                    return 0
+                }
             }
-          }
-          throw JSONDecodingError.malformedNumber
-        case asciiBackslash:
-          return nil
-        default:
-          return 0
-        }
-      }
-      return 0
-    case asciiOne...asciiNine: // 1...9
-      var n = 0 as UInt64
-      while index != end {
-        let digit = source[index]
-        switch digit {
-        case asciiZero...asciiNine: // 0...9
-          let val = UInt64(digit - asciiZero)
-          if n > UInt64.max / 10 || n * 10 > UInt64.max - val {
-            throw JSONDecodingError.numberRange
-          }
-          source.formIndex(after: &index)
-          n = n * 10 + val
-        case asciiPeriod, asciiLowerE, asciiUpperE: // . e
-          // Slow path: JSON allows floating-point notation for integers
-          index = start
-          if let d = try parseBareDouble(source: source,
-                                         index: &index,
-                                         end: end) {
-            if let u = UInt64(exactly: d) {
-              return u
+            return 0
+        case asciiOne...asciiNine:  // 1...9
+            var n = 0 as UInt64
+            while index != end {
+                let digit = source[index]
+                switch digit {
+                case asciiZero...asciiNine:  // 0...9
+                    let val = UInt64(digit - asciiZero)
+                    if n > UInt64.max / 10 || n * 10 > UInt64.max - val {
+                        throw JSONDecodingError.numberRange
+                    }
+                    source.formIndex(after: &index)
+                    n = n * 10 + val
+                case asciiPeriod, asciiLowerE, asciiUpperE:  // . e
+                    // Slow path: JSON allows floating-point notation for integers
+                    index = start
+                    if let d = try parseBareDouble(
+                        source: source,
+                        index: &index,
+                        end: end
+                    ) {
+                        if let u = UInt64(exactly: d) {
+                            return u
+                        }
+                    }
+                    throw JSONDecodingError.malformedNumber
+                case asciiBackslash:
+                    return nil
+                default:
+                    return n
+                }
             }
-          }
-          throw JSONDecodingError.malformedNumber
+            return n
         case asciiBackslash:
-          return nil
+            return nil
         default:
-          return n
+            throw JSONDecodingError.malformedNumber
         }
-      }
-      return n
-    case asciiBackslash:
-      return nil
-    default:
-      throw JSONDecodingError.malformedNumber
     }
-  }
 
-  // Parse the leading Int64 from the provided utf8.
-  //
-  // This uses parseBareUInt64() to do the heavy lifting;
-  // we just check for a leading minus and negate the result
-  // as necessary.
-  //
-  // As with parseBareUInt64(), if it encounters a "\" backslash
-  // character, it returns a nil.  This allows callers to use this to
-  // do a "fast-path" decode of simple quoted numbers by parsing the
-  // UTF8 directly, only falling back to a full String decode when
-  // absolutely necessary.
-  private func parseBareSInt64(
-    source: UnsafeRawBufferPointer,
-    index: inout UnsafeRawBufferPointer.Index,
-    end: UnsafeRawBufferPointer.Index
-  ) throws -> Int64? {
-    if index == end {
-      throw JSONDecodingError.truncated
-    }
-    let c = source[index]
-    if c == asciiMinus { // -
-      source.formIndex(after: &index)
-      if index == end {
-        throw JSONDecodingError.truncated
-      }
-      // character after '-' must be digit
-      let digit = source[index]
-      if digit < asciiZero || digit > asciiNine {
-        throw JSONDecodingError.malformedNumber
-      }
-      if let n = try parseBareUInt64(source: source, index: &index, end: end) {
-        let limit: UInt64 = 0x8000000000000000 // -Int64.min
-        if n >= limit {
-          if n > limit {
-            // Too large negative number
-            throw JSONDecodingError.numberRange
-          } else {
-            return Int64.min // Special case for Int64.min
-          }
-        }
-        return -Int64(bitPattern: n)
-      } else {
-        return nil
-      }
-    } else if let n = try parseBareUInt64(source: source, index: &index, end: end) {
-      if n > UInt64(bitPattern: Int64.max) {
-        throw JSONDecodingError.numberRange
-      }
-      return Int64(bitPattern: n)
-    } else {
-      return nil
+    // Parse the leading Int64 from the provided utf8.
+    //
+    // This uses parseBareUInt64() to do the heavy lifting;
+    // we just check for a leading minus and negate the result
+    // as necessary.
+    //
+    // As with parseBareUInt64(), if it encounters a "\" backslash
+    // character, it returns a nil.  This allows callers to use this to
+    // do a "fast-path" decode of simple quoted numbers by parsing the
+    // UTF8 directly, only falling back to a full String decode when
+    // absolutely necessary.
+    private func parseBareSInt64(
+        source: UnsafeRawBufferPointer,
+        index: inout UnsafeRawBufferPointer.Index,
+        end: UnsafeRawBufferPointer.Index
+    ) throws -> Int64? {
+        if index == end {
+            throw JSONDecodingError.truncated
+        }
+        let c = source[index]
+        if c == asciiMinus {  // -
+            source.formIndex(after: &index)
+            if index == end {
+                throw JSONDecodingError.truncated
+            }
+            // character after '-' must be digit
+            let digit = source[index]
+            if digit < asciiZero || digit > asciiNine {
+                throw JSONDecodingError.malformedNumber
+            }
+            if let n = try parseBareUInt64(source: source, index: &index, end: end) {
+                let limit: UInt64 = 0x8000_0000_0000_0000  // -Int64.min
+                if n >= limit {
+                    if n > limit {
+                        // Too large negative number
+                        throw JSONDecodingError.numberRange
+                    } else {
+                        return Int64.min  // Special case for Int64.min
+                    }
+                }
+                return -Int64(bitPattern: n)
+            } else {
+                return nil
+            }
+        } else if let n = try parseBareUInt64(source: source, index: &index, end: end) {
+            if n > UInt64(bitPattern: Int64.max) {
+                throw JSONDecodingError.numberRange
+            }
+            return Int64(bitPattern: n)
+        } else {
+            return nil
+        }
     }
-  }
 
-  // Identify a floating-point token in the upcoming UTF8 bytes.
-  //
-  // This implements the full grammar defined by the JSON RFC 7159.
-  // Note that Swift's string-to-number conversions are much more
-  // lenient, so this is necessary if we want to accurately reject
-  // malformed JSON numbers.
-  //
-  // This is used by nextDouble() and nextFloat() to parse double and
-  // floating-point values, including values that happen to be in quotes.
-  // It's also used by the slow path in parseBareSInt64() and parseBareUInt64()
-  // above to handle integer values that are written in float-point notation.
-  private func parseBareDouble(
-    source: UnsafeRawBufferPointer,
-    index: inout UnsafeRawBufferPointer.Index,
-    end: UnsafeRawBufferPointer.Index
-  ) throws -> Double? {
-    // RFC 7159 defines the grammar for JSON numbers as:
-    // number = [ minus ] int [ frac ] [ exp ]
-    if index == end {
-      throw JSONDecodingError.truncated
-    }
-    let start = index
-    var c = source[index]
-    if c == asciiBackslash {
-      return nil
-    }
+    // Identify a floating-point token in the upcoming UTF8 bytes.
+    //
+    // This implements the full grammar defined by the JSON RFC 7159.
+    // Note that Swift's string-to-number conversions are much more
+    // lenient, so this is necessary if we want to accurately reject
+    // malformed JSON numbers.
+    //
+    // This is used by nextDouble() and nextFloat() to parse double and
+    // floating-point values, including values that happen to be in quotes.
+    // It's also used by the slow path in parseBareSInt64() and parseBareUInt64()
+    // above to handle integer values that are written in float-point notation.
+    private func parseBareDouble(
+        source: UnsafeRawBufferPointer,
+        index: inout UnsafeRawBufferPointer.Index,
+        end: UnsafeRawBufferPointer.Index
+    ) throws -> Double? {
+        // RFC 7159 defines the grammar for JSON numbers as:
+        // number = [ minus ] int [ frac ] [ exp ]
+        if index == end {
+            throw JSONDecodingError.truncated
+        }
+        let start = index
+        var c = source[index]
+        if c == asciiBackslash {
+            return nil
+        }
 
-    // Optional leading minus sign
-    if c == asciiMinus { // -
-      source.formIndex(after: &index)
-      if index == end {
-        index = start
-        throw JSONDecodingError.truncated
-      }
-      c = source[index]
-      if c == asciiBackslash {
-        return nil
-      }
-    } else if c == asciiUpperN { // Maybe NaN?
-      // Return nil, let the caller deal with it.
-      return nil
-    }
+        // Optional leading minus sign
+        if c == asciiMinus {  // -
+            source.formIndex(after: &index)
+            if index == end {
+                index = start
+                throw JSONDecodingError.truncated
+            }
+            c = source[index]
+            if c == asciiBackslash {
+                return nil
+            }
+        } else if c == asciiUpperN {  // Maybe NaN?
+            // Return nil, let the caller deal with it.
+            return nil
+        }
 
-    if c == asciiUpperI { // Maybe Infinity, Inf, -Infinity, or -Inf ?
-      // Return nil, let the caller deal with it.
-      return nil
-    }
+        if c == asciiUpperI {  // Maybe Infinity, Inf, -Infinity, or -Inf ?
+            // Return nil, let the caller deal with it.
+            return nil
+        }
 
-    // Integer part can be zero or a series of digits not starting with zero
-    // int = zero / (digit1-9 *DIGIT)
-    switch c {
-    case asciiZero:
-      // First digit can be zero only if not followed by a digit
-      source.formIndex(after: &index)
-      if index == end {
-        return 0.0
-      }
-      c = source[index]
-      if c == asciiBackslash {
-        return nil
-      }
-      if c >= asciiZero && c <= asciiNine {
-        throw JSONDecodingError.leadingZero
-      }
-    case asciiOne...asciiNine:
-      while c >= asciiZero && c <= asciiNine {
-        source.formIndex(after: &index)
-        if index == end {
-          if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
+        // Integer part can be zero or a series of digits not starting with zero
+        // int = zero / (digit1-9 *DIGIT)
+        switch c {
+        case asciiZero:
+            // First digit can be zero only if not followed by a digit
+            source.formIndex(after: &index)
+            if index == end {
+                return 0.0
+            }
+            c = source[index]
+            if c == asciiBackslash {
+                return nil
+            }
+            if c >= asciiZero && c <= asciiNine {
+                throw JSONDecodingError.leadingZero
+            }
+        case asciiOne...asciiNine:
+            while c >= asciiZero && c <= asciiNine {
+                source.formIndex(after: &index)
+                if index == end {
+                    if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
+                        return d
+                    } else {
+                        throw JSONDecodingError.invalidUTF8
+                    }
+                }
+                c = source[index]
+                if c == asciiBackslash {
+                    return nil
+                }
+            }
+        default:
+            // Integer part cannot be empty
+            throw JSONDecodingError.malformedNumber
+        }
+
+        // frac = decimal-point 1*DIGIT
+        if c == asciiPeriod {
+            source.formIndex(after: &index)
+            if index == end {
+                // decimal point must have a following digit
+                throw JSONDecodingError.truncated
+            }
+            c = source[index]
+            switch c {
+            case asciiZero...asciiNine:  // 0...9
+                while c >= asciiZero && c <= asciiNine {
+                    source.formIndex(after: &index)
+                    if index == end {
+                        if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
+                            return d
+                        } else {
+                            throw JSONDecodingError.invalidUTF8
+                        }
+                    }
+                    c = source[index]
+                    if c == asciiBackslash {
+                        return nil
+                    }
+                }
+            case asciiBackslash:
+                return nil
+            default:
+                // decimal point must be followed by at least one digit
+                throw JSONDecodingError.malformedNumber
+            }
+        }
+
+        // exp = e [ minus / plus ] 1*DIGIT
+        if c == asciiLowerE || c == asciiUpperE {
+            source.formIndex(after: &index)
+            if index == end {
+                // "e" must be followed by +,-, or digit
+                throw JSONDecodingError.truncated
+            }
+            c = source[index]
+            if c == asciiBackslash {
+                return nil
+            }
+            if c == asciiPlus || c == asciiMinus {  // + -
+                source.formIndex(after: &index)
+                if index == end {
+                    // must be at least one digit in exponent
+                    throw JSONDecodingError.truncated
+                }
+                c = source[index]
+                if c == asciiBackslash {
+                    return nil
+                }
+            }
+            switch c {
+            case asciiZero...asciiNine:
+                while c >= asciiZero && c <= asciiNine {
+                    source.formIndex(after: &index)
+                    if index == end {
+                        if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
+                            return d
+                        } else {
+                            throw JSONDecodingError.invalidUTF8
+                        }
+                    }
+                    c = source[index]
+                    if c == asciiBackslash {
+                        return nil
+                    }
+                }
+            default:
+                // must be at least one digit in exponent
+                throw JSONDecodingError.malformedNumber
+            }
+        }
+        if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
             return d
-          } else {
+        } else {
             throw JSONDecodingError.invalidUTF8
-          }
         }
-        c = source[index]
-        if c == asciiBackslash {
-          return nil
+    }
+
+    /// Returns a fully-parsed string with all backslash escapes
+    /// correctly processed, or nil if next token is not a string.
+    ///
+    /// Assumes the leading quote has been verified (but not consumed)
+    private mutating func parseOptionalQuotedString() -> String? {
+        // Caller has already asserted that currentByte == quote here
+        var sawBackslash = false
+        advance()
+        let start = index
+        while hasMoreContent {
+            switch currentByte {
+            case asciiDoubleQuote:  // "
+                let s = utf8ToString(bytes: source, start: start, end: index)
+                advance()
+                if let t = s {
+                    if sawBackslash {
+                        return decodeString(t)
+                    } else {
+                        return t
+                    }
+                } else {
+                    return nil  // Invalid UTF8
+                }
+            case asciiBackslash:  //  \
+                advance()
+                guard hasMoreContent else {
+                    return nil  // Unterminated escape
+                }
+                sawBackslash = true
+            default:
+                break
+            }
+            advance()
         }
-      }
-    default:
-      // Integer part cannot be empty
-      throw JSONDecodingError.malformedNumber
+        return nil  // Unterminated quoted string
     }
 
-    // frac = decimal-point 1*DIGIT
-    if c == asciiPeriod {
-      source.formIndex(after: &index)
-      if index == end {
-        // decimal point must have a following digit
-        throw JSONDecodingError.truncated
-      }
-      c = source[index]
-      switch c {
-      case asciiZero...asciiNine: // 0...9
-        while c >= asciiZero && c <= asciiNine {
-          source.formIndex(after: &index)
-          if index == end {
-            if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
-              return d
+    /// Parse an unsigned integer, whether or not its quoted.
+    /// This also handles cases such as quoted numbers that have
+    /// backslash escapes in them.
+    ///
+    /// This supports the full range of UInt64 (whether quoted or not)
+    /// unless the number is written in floating-point format.  In that
+    /// case, we decode it with only Double precision.
+    internal mutating func nextUInt() throws -> UInt64 {
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
+        }
+        let c = currentByte
+        if c == asciiDoubleQuote {
+            let start = index
+            advance()
+            if let u = try parseBareUInt64(
+                source: source,
+                index: &index,
+                end: source.endIndex
+            ) {
+                guard hasMoreContent else {
+                    throw JSONDecodingError.truncated
+                }
+                if currentByte != asciiDoubleQuote {
+                    throw JSONDecodingError.malformedNumber
+                }
+                advance()
+                return u
             } else {
-              throw JSONDecodingError.invalidUTF8
+                // Couldn't parse because it had a "\" in the string,
+                // so parse out the quoted string and then reparse
+                // the result to get a UInt.
+                index = start
+                let s = try nextQuotedString()
+                let raw = s.data(using: String.Encoding.utf8)!
+                let n = try raw.withUnsafeBytes {
+                    (body: UnsafeRawBufferPointer) -> UInt64? in
+                    if body.count > 0 {
+                        var index = body.startIndex
+                        let end = body.endIndex
+                        if let u = try parseBareUInt64(
+                            source: body,
+                            index: &index,
+                            end: end
+                        ) {
+                            if index == end {
+                                return u
+                            }
+                        }
+                    }
+                    return nil
+                }
+                if let n = n {
+                    return n
+                }
             }
-          }
-          c = source[index]
-          if c == asciiBackslash {
-            return nil
-          }
+        } else if let u = try parseBareUInt64(
+            source: source,
+            index: &index,
+            end: source.endIndex
+        ) {
+            return u
         }
-      case asciiBackslash:
-        return nil
-      default:
-        // decimal point must be followed by at least one digit
         throw JSONDecodingError.malformedNumber
-      }
     }
 
-    // exp = e [ minus / plus ] 1*DIGIT
-    if c == asciiLowerE || c == asciiUpperE {
-      source.formIndex(after: &index)
-      if index == end {
-        // "e" must be followed by +,-, or digit
-        throw JSONDecodingError.truncated
-      }
-      c = source[index]
-      if c == asciiBackslash {
-        return nil
-      }
-      if c == asciiPlus || c == asciiMinus { // + -
-        source.formIndex(after: &index)
-        if index == end {
-          // must be at least one digit in exponent
-          throw JSONDecodingError.truncated
+    /// Parse a signed integer, quoted or not, including handling
+    /// backslash escapes for quoted values.
+    ///
+    /// This supports the full range of Int64 (whether quoted or not)
+    /// unless the number is written in floating-point format.  In that
+    /// case, we decode it with only Double precision.
+    internal mutating func nextSInt() throws -> Int64 {
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
         }
-        c = source[index]
-        if c == asciiBackslash {
-          return nil
-        }
-      }
-      switch c {
-      case asciiZero...asciiNine:
-        while c >= asciiZero && c <= asciiNine {
-          source.formIndex(after: &index)
-          if index == end {
-            if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
-              return d
+        let c = currentByte
+        if c == asciiDoubleQuote {
+            let start = index
+            advance()
+            if let s = try parseBareSInt64(
+                source: source,
+                index: &index,
+                end: source.endIndex
+            ) {
+                guard hasMoreContent else {
+                    throw JSONDecodingError.truncated
+                }
+                if currentByte != asciiDoubleQuote {
+                    throw JSONDecodingError.malformedNumber
+                }
+                advance()
+                return s
             } else {
-              throw JSONDecodingError.invalidUTF8
+                // Couldn't parse because it had a "\" in the string,
+                // so parse out the quoted string and then reparse
+                // the result as an SInt.
+                index = start
+                let s = try nextQuotedString()
+                let raw = s.data(using: String.Encoding.utf8)!
+                let n = try raw.withUnsafeBytes {
+                    (body: UnsafeRawBufferPointer) -> Int64? in
+                    if body.count > 0 {
+                        var index = body.startIndex
+                        let end = body.endIndex
+                        if let s = try parseBareSInt64(
+                            source: body,
+                            index: &index,
+                            end: end
+                        ) {
+                            if index == end {
+                                return s
+                            }
+                        }
+                    }
+                    return nil
+                }
+                if let n = n {
+                    return n
+                }
             }
-          }
-          c = source[index]
-          if c == asciiBackslash {
-            return nil
-          }
+        } else if let s = try parseBareSInt64(
+            source: source,
+            index: &index,
+            end: source.endIndex
+        ) {
+            return s
         }
-      default:
-        // must be at least one digit in exponent
         throw JSONDecodingError.malformedNumber
-      }
     }
-    if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) {
-      return d
-    } else {
-      throw JSONDecodingError.invalidUTF8
+
+    /// Parse the next Float value, regardless of whether it
+    /// is quoted, including handling backslash escapes for
+    /// quoted strings.
+    internal mutating func nextFloat() throws -> Float {
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
+        }
+        let c = currentByte
+        if c == asciiDoubleQuote {  // "
+            let start = index
+            advance()
+            if let d = try parseBareDouble(
+                source: source,
+                index: &index,
+                end: source.endIndex
+            ) {
+                guard hasMoreContent else {
+                    throw JSONDecodingError.truncated
+                }
+                if currentByte != asciiDoubleQuote {
+                    throw JSONDecodingError.malformedNumber
+                }
+                advance()
+                return Float(d)
+            } else {
+                // Slow Path: parseBareDouble returned nil: It might be
+                // a valid float, but had something that
+                // parseBareDouble cannot directly handle.  So we reset,
+                // try a full string parse, then examine the result:
+                index = start
+                let s = try nextQuotedString()
+                switch s {
+                case "NaN": return Float.nan
+                case "Inf": return Float.infinity
+                case "-Inf": return -Float.infinity
+                case "Infinity": return Float.infinity
+                case "-Infinity": return -Float.infinity
+                default:
+                    let raw = s.data(using: String.Encoding.utf8)!
+                    let n = try raw.withUnsafeBytes {
+                        (body: UnsafeRawBufferPointer) -> Float? in
+                        if body.count > 0 {
+                            var index = body.startIndex
+                            let end = body.endIndex
+                            if let d = try parseBareDouble(
+                                source: body,
+                                index: &index,
+                                end: end
+                            ) {
+                                let f = Float(d)
+                                if index == end && f.isFinite {
+                                    return f
+                                }
+                            }
+                        }
+                        return nil
+                    }
+                    if let n = n {
+                        return n
+                    }
+                }
+            }
+        } else {
+            if let d = try parseBareDouble(
+                source: source,
+                index: &index,
+                end: source.endIndex
+            ) {
+                let f = Float(d)
+                if f.isFinite {
+                    return f
+                }
+            }
+        }
+        throw JSONDecodingError.malformedNumber
     }
-  }
 
-  /// Returns a fully-parsed string with all backslash escapes
-  /// correctly processed, or nil if next token is not a string.
-  ///
-  /// Assumes the leading quote has been verified (but not consumed)
-  private mutating func parseOptionalQuotedString() -> String? {
-    // Caller has already asserted that currentByte == quote here
-    var sawBackslash = false
-    advance()
-    let start = index
-    while hasMoreContent {
-      switch currentByte {
-      case asciiDoubleQuote: // "
-        let s = utf8ToString(bytes: source, start: start, end: index)
-        advance()
-        if let t = s {
-          if sawBackslash {
-            return decodeString(t)
-          } else {
-            return t
-          }
+    /// Parse the next Double value, regardless of whether it
+    /// is quoted, including handling backslash escapes for
+    /// quoted strings.
+    internal mutating func nextDouble() throws -> Double {
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
+        }
+        let c = currentByte
+        if c == asciiDoubleQuote {  // "
+            let start = index
+            advance()
+            if let d = try parseBareDouble(
+                source: source,
+                index: &index,
+                end: source.endIndex
+            ) {
+                guard hasMoreContent else {
+                    throw JSONDecodingError.truncated
+                }
+                if currentByte != asciiDoubleQuote {
+                    throw JSONDecodingError.malformedNumber
+                }
+                advance()
+                return d
+            } else {
+                // Slow Path: parseBareDouble returned nil: It might be
+                // a valid float, but had something that
+                // parseBareDouble cannot directly handle.  So we reset,
+                // try a full string parse, then examine the result:
+                index = start
+                let s = try nextQuotedString()
+                switch s {
+                case "NaN": return Double.nan
+                case "Inf": return Double.infinity
+                case "-Inf": return -Double.infinity
+                case "Infinity": return Double.infinity
+                case "-Infinity": return -Double.infinity
+                default:
+                    let raw = s.data(using: String.Encoding.utf8)!
+                    let n = try raw.withUnsafeBytes {
+                        (body: UnsafeRawBufferPointer) -> Double? in
+                        if body.count > 0 {
+                            var index = body.startIndex
+                            let end = body.endIndex
+                            if let d = try parseBareDouble(
+                                source: body,
+                                index: &index,
+                                end: end
+                            ) {
+                                if index == end {
+                                    return d
+                                }
+                            }
+                        }
+                        return nil
+                    }
+                    if let n = n {
+                        return n
+                    }
+                }
+            }
         } else {
-          return nil // Invalid UTF8
+            if let d = try parseBareDouble(
+                source: source,
+                index: &index,
+                end: source.endIndex
+            ) {
+                return d
+            }
         }
-      case asciiBackslash: //  \
-        advance()
+        throw JSONDecodingError.malformedNumber
+    }
+
+    /// Return the contents of the following quoted string,
+    /// or throw an error if the next token is not a string.
+    internal mutating func nextQuotedString() throws -> String {
+        skipWhitespace()
         guard hasMoreContent else {
-          return nil // Unterminated escape
+            throw JSONDecodingError.truncated
+        }
+        let c = currentByte
+        if c != asciiDoubleQuote {
+            throw JSONDecodingError.malformedString
+        }
+        if let s = parseOptionalQuotedString() {
+            return s
+        } else {
+            throw JSONDecodingError.malformedString
         }
-        sawBackslash = true
-      default:
-        break
-      }
-      advance()
     }
-    return nil // Unterminated quoted string
-  }
 
-  /// Parse an unsigned integer, whether or not its quoted.
-  /// This also handles cases such as quoted numbers that have
-  /// backslash escapes in them.
-  ///
-  /// This supports the full range of UInt64 (whether quoted or not)
-  /// unless the number is written in floating-point format.  In that
-  /// case, we decode it with only Double precision.
-  internal mutating func nextUInt() throws -> UInt64 {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
+    /// Return the contents of the following quoted string,
+    /// or nil if the next token is not a string.
+    /// This will only throw an error if the next token starts
+    /// out as a string but is malformed in some way.
+    internal mutating func nextOptionalQuotedString() throws -> String? {
+        skipWhitespace()
+        guard hasMoreContent else {
+            return nil
+        }
+        let c = currentByte
+        if c != asciiDoubleQuote {
+            return nil
+        }
+        return try nextQuotedString()
     }
-    let c = currentByte
-    if c == asciiDoubleQuote {
-      let start = index
-      advance()
-      if let u = try parseBareUInt64(source: source,
-                                     index: &index,
-                                     end: source.endIndex) {
+
+    /// Return a Data with the decoded contents of the
+    /// following base-64 string.
+    ///
+    /// Notes on Google's implementation:
+    ///  * Google's C++ implementation accepts arbitrary whitespace
+    ///    mixed in with the base-64 characters
+    ///  * Google's C++ implementation ignores missing '=' characters
+    ///    but if present, there must be the exact correct number of them.
+    ///  * Google's C++ implementation accepts both "regular" and
+    ///    "web-safe" base-64 variants (it seems to prefer the
+    ///    web-safe version as defined in RFC 4648
+    internal mutating func nextBytesValue() throws -> Data {
+        skipWhitespace()
         guard hasMoreContent else {
-          throw JSONDecodingError.truncated
+            throw JSONDecodingError.truncated
         }
-        if currentByte != asciiDoubleQuote {
-          throw JSONDecodingError.malformedNumber
+        return try parseBytes(source: source, index: &index, end: source.endIndex)
+    }
+
+    /// Private function to help parse keywords.
+    private mutating func skipOptionalKeyword(bytes: [UInt8]) -> Bool {
+        let start = index
+        for b in bytes {
+            guard hasMoreContent else {
+                index = start
+                return false
+            }
+            let c = currentByte
+            if c != b {
+                index = start
+                return false
+            }
+            advance()
         }
-        advance()
-        return u
-      } else {
-        // Couldn't parse because it had a "\" in the string,
-        // so parse out the quoted string and then reparse
-        // the result to get a UInt.
-        index = start
-        let s = try nextQuotedString()
-        let raw = s.data(using: String.Encoding.utf8)!
-        let n = try raw.withUnsafeBytes {
-          (body: UnsafeRawBufferPointer) -> UInt64? in
-          if body.count > 0 {
-            var index = body.startIndex
-            let end = body.endIndex
-            if let u = try parseBareUInt64(source: body,
-                                           index: &index,
-                                           end: end) {
-              if index == end {
-                return u
-              }
+        if hasMoreContent {
+            let c = currentByte
+            if (c >= asciiUpperA && c <= asciiUpperZ) || (c >= asciiLowerA && c <= asciiLowerZ) {
+                index = start
+                return false
             }
-          }
-          return nil
         }
-        if let n = n {
-          return n
+        return true
+    }
+
+    /// If the next token is the identifier "null", consume it and return true.
+    internal mutating func skipOptionalNull() -> Bool {
+        skipWhitespace()
+        if hasMoreContent && currentByte == asciiLowerN {
+            return skipOptionalKeyword(bytes: [
+                asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL,
+            ])
         }
-      }
-    } else if let u = try parseBareUInt64(source: source,
-                                          index: &index,
-                                          end: source.endIndex) {
-      return u
+        return false
     }
-    throw JSONDecodingError.malformedNumber
-  }
 
-  /// Parse a signed integer, quoted or not, including handling
-  /// backslash escapes for quoted values.
-  ///
-  /// This supports the full range of Int64 (whether quoted or not)
-  /// unless the number is written in floating-point format.  In that
-  /// case, we decode it with only Double precision.
-  internal mutating func nextSInt() throws -> Int64 {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
+    /// Return the following Bool "true" or "false", including
+    /// full processing of quoted boolean values.  (Used in map
+    /// keys, for instance.)
+    internal mutating func nextBool() throws -> Bool {
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
+        }
+        let c = currentByte
+        switch c {
+        case asciiLowerF:  // f
+            if skipOptionalKeyword(bytes: [
+                asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE,
+            ]) {
+                return false
+            }
+        case asciiLowerT:  // t
+            if skipOptionalKeyword(bytes: [
+                asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE,
+            ]) {
+                return true
+            }
+        default:
+            break
+        }
+        throw JSONDecodingError.malformedBool
     }
-    let c = currentByte
-    if c == asciiDoubleQuote {
-      let start = index
-      advance()
-      if let s = try parseBareSInt64(source: source,
-                                     index: &index,
-                                     end: source.endIndex) {
+
+    /// Return the following Bool "true" or "false", including
+    /// full processing of quoted boolean values.  (Used in map
+    /// keys, for instance.)
+    internal mutating func nextQuotedBool() throws -> Bool {
+        skipWhitespace()
         guard hasMoreContent else {
-          throw JSONDecodingError.truncated
+            throw JSONDecodingError.truncated
         }
         if currentByte != asciiDoubleQuote {
-          throw JSONDecodingError.malformedNumber
+            throw JSONDecodingError.unquotedMapKey
         }
-        advance()
-        return s
-      } else {
-        // Couldn't parse because it had a "\" in the string,
-        // so parse out the quoted string and then reparse
-        // the result as an SInt.
-        index = start
-        let s = try nextQuotedString()
-        let raw = s.data(using: String.Encoding.utf8)!
-        let n = try raw.withUnsafeBytes {
-          (body: UnsafeRawBufferPointer) -> Int64? in
-          if body.count > 0 {
-            var index = body.startIndex
-            let end = body.endIndex
-            if let s = try parseBareSInt64(source: body,
-                                           index: &index,
-                                           end: end) {
-              if index == end {
-                return s
-              }
+        if let s = parseOptionalQuotedString() {
+            switch s {
+            case "false": return false
+            case "true": return true
+            default: break
             }
-          }
-          return nil
-        }
-        if let n = n {
-          return n
         }
-      }
-    } else if let s = try parseBareSInt64(source: source,
-                                          index: &index,
-                                          end: source.endIndex) {
-      return s
+        throw JSONDecodingError.malformedBool
     }
-    throw JSONDecodingError.malformedNumber
-  }
 
-  /// Parse the next Float value, regardless of whether it
-  /// is quoted, including handling backslash escapes for
-  /// quoted strings.
-  internal mutating func nextFloat() throws -> Float {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
-    }
-    let c = currentByte
-    if c == asciiDoubleQuote { // "
-      let start = index
-      advance()
-      if let d = try parseBareDouble(source: source,
-                                     index: &index,
-                                     end: source.endIndex) {
+    /// Returns pointer/count spanning the UTF8 bytes of the next regular
+    /// key or nil if the key contains a backslash (and therefore requires
+    /// the full string-parsing logic to properly parse).
+    private mutating func nextOptionalKey() throws -> UnsafeRawBufferPointer? {
+        skipWhitespace()
+        let stringStart = index
         guard hasMoreContent else {
-          throw JSONDecodingError.truncated
+            throw JSONDecodingError.truncated
         }
         if currentByte != asciiDoubleQuote {
-          throw JSONDecodingError.malformedNumber
+            return nil
         }
         advance()
-        return Float(d)
-      } else {
-        // Slow Path: parseBareDouble returned nil: It might be
-        // a valid float, but had something that
-        // parseBareDouble cannot directly handle.  So we reset,
-        // try a full string parse, then examine the result:
-        index = start
-        let s = try nextQuotedString()
-        switch s {
-        case "NaN": return Float.nan
-        case "Inf": return Float.infinity
-        case "-Inf": return -Float.infinity
-        case "Infinity": return Float.infinity
-        case "-Infinity": return -Float.infinity
-        default:
-          let raw = s.data(using: String.Encoding.utf8)!
-          let n = try raw.withUnsafeBytes {
-            (body: UnsafeRawBufferPointer) -> Float? in
-            if body.count > 0 {
-              var index = body.startIndex
-              let end = body.endIndex
-              if let d = try parseBareDouble(source: body,
-                                             index: &index,
-                                             end: end) {
-                let f = Float(d)
-                if index == end && f.isFinite {
-                  return f
-                }
-              }
+        let nameStart = index
+        while hasMoreContent && currentByte != asciiDoubleQuote {
+            if currentByte == asciiBackslash {
+                index = stringStart  // Reset to open quote
+                return nil
             }
-            return nil
-          }
-          if let n = n {
-            return n
-          }
-        }
-      }
-    } else {
-      if let d = try parseBareDouble(source: source,
-                                     index: &index,
-                                     end: source.endIndex) {
-        let f = Float(d)
-        if f.isFinite {
-          return f
-        }
-      }
+            advance()
+        }
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
+        }
+        let buff = UnsafeRawBufferPointer(
+            start: source.baseAddress! + nameStart,
+            count: index - nameStart
+        )
+        advance()
+        return buff
     }
-    throw JSONDecodingError.malformedNumber
-  }
 
-  /// Parse the next Double value, regardless of whether it
-  /// is quoted, including handling backslash escapes for
-  /// quoted strings.
-  internal mutating func nextDouble() throws -> Double {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
+    /// Parse a field name, look it up in the provided field name map,
+    /// and return the corresponding field number.
+    ///
+    /// Throws if field name cannot be parsed.
+    /// If it encounters an unknown field name, it throws
+    /// unless `options.ignoreUnknownFields` is set, in which case
+    /// it silently skips it.
+    internal mutating func nextFieldNumber(
+        names: _NameMap,
+        messageType: any Message.Type
+    ) throws -> Int? {
+        while true {
+            var fieldName: String
+            if let key = try nextOptionalKey() {
+                // Fast path:  We parsed it as UTF8 bytes...
+                try skipRequiredCharacter(asciiColon)  // :
+                if let fieldNumber = names.number(forJSONName: key) {
+                    return fieldNumber
+                }
+                if let s = utf8ToString(bytes: key.baseAddress!, count: key.count) {
+                    fieldName = s
+                } else {
+                    throw JSONDecodingError.invalidUTF8
+                }
+            } else {
+                // Slow path:  We parsed a String; lookups from String are slower.
+                fieldName = try nextQuotedString()
+                try skipRequiredCharacter(asciiColon)  // :
+                if let fieldNumber = names.number(forJSONName: fieldName) {
+                    return fieldNumber
+                }
+            }
+            if let first = fieldName.utf8.first, first == UInt8(ascii: "["),
+                let last = fieldName.utf8.last, last == UInt8(ascii: "]")
+            {
+                fieldName.removeFirst()
+                fieldName.removeLast()
+                if let fieldNumber = extensions.fieldNumberForProto(messageType: messageType, protoFieldName: fieldName)
+                {
+                    return fieldNumber
+                }
+            }
+            if !options.ignoreUnknownFields {
+                throw JSONDecodingError.unknownField(fieldName)
+            }
+            // Unknown field, skip it and try to parse the next field name
+            try skipValue()
+            if skipOptionalObjectEnd() {
+                return nil
+            }
+            try skipRequiredComma()
+        }
     }
-    let c = currentByte
-    if c == asciiDoubleQuote { // "
-      let start = index
-      advance()
-      if let d = try parseBareDouble(source: source,
-                                     index: &index,
-                                     end: source.endIndex) {
-        guard hasMoreContent else {
-          throw JSONDecodingError.truncated
+
+    /// Parse the next token as a string or numeric enum value.  Throws
+    /// unrecognizedEnumValue if the string/number can't initialize the
+    /// enum.  Will throw other errors if the JSON is malformed.
+    internal mutating func nextEnumValue<E: Enum>() throws -> E? {
+        func throwOrIgnore() throws -> E? {
+            if options.ignoreUnknownFields {
+                return nil
+            } else {
+                throw JSONDecodingError.unrecognizedEnumValue
+            }
         }
-        if currentByte != asciiDoubleQuote {
-          throw JSONDecodingError.malformedNumber
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
         }
-        advance()
-        return d
-      } else {
-        // Slow Path: parseBareDouble returned nil: It might be
-        // a valid float, but had something that
-        // parseBareDouble cannot directly handle.  So we reset,
-        // try a full string parse, then examine the result:
-        index = start
-        let s = try nextQuotedString()
-        switch s {
-        case "NaN": return Double.nan
-        case "Inf": return Double.infinity
-        case "-Inf": return -Double.infinity
-        case "Infinity": return Double.infinity
-        case "-Infinity": return -Double.infinity
-        default:
-          let raw = s.data(using: String.Encoding.utf8)!
-          let n = try raw.withUnsafeBytes {
-            (body: UnsafeRawBufferPointer) -> Double? in
-            if body.count > 0 {
-              var index = body.startIndex
-              let end = body.endIndex
-              if let d = try parseBareDouble(source: body,
-                                             index: &index,
-                                             end: end) {
-                if index == end {
-                  return d
+        if currentByte == asciiDoubleQuote {
+            if let name = try nextOptionalKey() {
+                if let e = E(rawUTF8: name) {
+                    return e
+                } else {
+                    return try throwOrIgnore()
                 }
-              }
             }
-            return nil
-          }
-          if let n = n {
-            return n
-          }
-        }
-      }
-    } else {
-      if let d = try parseBareDouble(source: source,
-                                     index: &index,
-                                     end: source.endIndex) {
-        return d
-      }
+            let name = try nextQuotedString()
+            if let e = E(name: name) {
+                return e
+            } else {
+                return try throwOrIgnore()
+            }
+        } else {
+            let n = try nextSInt()
+            if let i = Int(exactly: n) {
+                if let e = E(rawValue: i) {
+                    return e
+                } else {
+                    return try throwOrIgnore()
+                }
+            } else {
+                throw JSONDecodingError.numberRange
+            }
+        }
     }
-    throw JSONDecodingError.malformedNumber
-  }
 
-  /// Return the contents of the following quoted string,
-  /// or throw an error if the next token is not a string.
-  internal mutating func nextQuotedString() throws -> String {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
-    }
-    let c = currentByte
-    if c != asciiDoubleQuote {
-      throw JSONDecodingError.malformedString
-    }
-    if let s = parseOptionalQuotedString() {
-      return s
-    } else {
-      throw JSONDecodingError.malformedString
+    /// Helper for skipping a single-character token.
+    private mutating func skipRequiredCharacter(_ required: UInt8) throws {
+        skipWhitespace()
+        guard hasMoreContent else {
+            throw JSONDecodingError.truncated
+        }
+        let next = currentByte
+        if next == required {
+            advance()
+            return
+        }
+        throw JSONDecodingError.failure
     }
-  }
 
-  /// Return the contents of the following quoted string,
-  /// or nil if the next token is not a string.
-  /// This will only throw an error if the next token starts
-  /// out as a string but is malformed in some way.
-  internal mutating func nextOptionalQuotedString() throws -> String? {
-    skipWhitespace()
-    guard hasMoreContent else {
-      return nil
+    /// Skip "{", throw if that's not the next character.
+    internal mutating func skipRequiredObjectStart() throws {
+        try skipRequiredCharacter(asciiOpenCurlyBracket)  // {
+        try incrementRecursionDepth()
     }
-    let c = currentByte
-    if c != asciiDoubleQuote {
-      return nil
-    }
-    return try nextQuotedString()
-  }
 
-  /// Return a Data with the decoded contents of the
-  /// following base-64 string.
-  ///
-  /// Notes on Google's implementation:
-  ///  * Google's C++ implementation accepts arbitrary whitespace
-  ///    mixed in with the base-64 characters
-  ///  * Google's C++ implementation ignores missing '=' characters
-  ///    but if present, there must be the exact correct number of them.
-  ///  * Google's C++ implementation accepts both "regular" and
-  ///    "web-safe" base-64 variants (it seems to prefer the
-  ///    web-safe version as defined in RFC 4648
-  internal mutating func nextBytesValue() throws -> Data {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
+    /// Skip ",", throw if that's not the next character.
+    internal mutating func skipRequiredComma() throws {
+        try skipRequiredCharacter(asciiComma)
     }
-    return try parseBytes(source: source, index: &index, end: source.endIndex)
-  }
 
-  /// Private function to help parse keywords.
-  private mutating func skipOptionalKeyword(bytes: [UInt8]) -> Bool {
-    let start = index
-    for b in bytes {
-      guard hasMoreContent else {
-        index = start
-        return false
-      }
-      let c = currentByte
-      if c != b {
-        index = start
-        return false
-      }
-      advance()
-    }
-    if hasMoreContent {
-      let c = currentByte
-      if (c >= asciiUpperA && c <= asciiUpperZ) ||
-        (c >= asciiLowerA && c <= asciiLowerZ) {
-        index = start
-        return false
-      }
+    /// Skip ":", throw if that's not the next character.
+    internal mutating func skipRequiredColon() throws {
+        try skipRequiredCharacter(asciiColon)
     }
-    return true
-  }
 
-  /// If the next token is the identifier "null", consume it and return true.
-  internal mutating func skipOptionalNull() -> Bool {
-    skipWhitespace()
-    if hasMoreContent && currentByte == asciiLowerN {
-      return skipOptionalKeyword(bytes: [
-        asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL
-      ])
+    /// Skip "[", throw if that's not the next character.
+    internal mutating func skipRequiredArrayStart() throws {
+        try skipRequiredCharacter(asciiOpenSquareBracket)  // [
     }
-    return false
-  }
 
-  /// Return the following Bool "true" or "false", including
-  /// full processing of quoted boolean values.  (Used in map
-  /// keys, for instance.)
-  internal mutating func nextBool() throws -> Bool {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
-    }
-    let c = currentByte
-    switch c {
-    case asciiLowerF: // f
-      if skipOptionalKeyword(bytes: [
-        asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE
-      ]) {
+    /// Helper for skipping optional single-character tokens.
+    private mutating func skipOptionalCharacter(_ c: UInt8) -> Bool {
+        skipWhitespace()
+        if hasMoreContent && currentByte == c {
+            advance()
+            return true
+        }
         return false
-      }
-    case asciiLowerT: // t
-      if skipOptionalKeyword(bytes: [
-        asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE
-      ]) {
-        return true
-      }
-    default:
-      break
     }
-    throw JSONDecodingError.malformedBool
-  }
 
-  /// Return the following Bool "true" or "false", including
-  /// full processing of quoted boolean values.  (Used in map
-  /// keys, for instance.)
-  internal mutating func nextQuotedBool() throws -> Bool {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
+    /// If the next non-whitespace character is "[", skip it
+    /// and return true.  Otherwise, return false.
+    internal mutating func skipOptionalArrayStart() -> Bool {
+        skipOptionalCharacter(asciiOpenSquareBracket)
     }
-    if currentByte != asciiDoubleQuote {
-      throw JSONDecodingError.unquotedMapKey
-    }
-    if let s = parseOptionalQuotedString() {
-      switch s {
-      case "false": return false
-      case "true": return true
-      default: break
-      }
-    }
-    throw JSONDecodingError.malformedBool
-  }
 
-  /// Returns pointer/count spanning the UTF8 bytes of the next regular
-  /// key or nil if the key contains a backslash (and therefore requires
-  /// the full string-parsing logic to properly parse).
-  private mutating func nextOptionalKey() throws -> UnsafeRawBufferPointer? {
-    skipWhitespace()
-    let stringStart = index
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
-    }
-    if currentByte != asciiDoubleQuote {
-      return nil
-    }
-    advance()
-    let nameStart = index
-    while hasMoreContent && currentByte != asciiDoubleQuote {
-      if currentByte == asciiBackslash {
-        index = stringStart // Reset to open quote
-        return nil
-      }
-      advance()
-    }
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
+    /// If the next non-whitespace character is "]", skip it
+    /// and return true.  Otherwise, return false.
+    internal mutating func skipOptionalArrayEnd() -> Bool {
+        skipOptionalCharacter(asciiCloseSquareBracket)  // ]// ]
     }
-    let buff = UnsafeRawBufferPointer(
-      start: source.baseAddress! + nameStart,
-      count: index - nameStart)
-    advance()
-    return buff
-  }
 
-  /// Parse a field name, look it up in the provided field name map,
-  /// and return the corresponding field number.
-  ///
-  /// Throws if field name cannot be parsed.
-  /// If it encounters an unknown field name, it throws
-  /// unless `options.ignoreUnknownFields` is set, in which case
-  /// it silently skips it.
-  internal mutating func nextFieldNumber(
-    names: _NameMap,
-    messageType: any Message.Type
-  ) throws -> Int? {
-    while true {
-      var fieldName: String
-      if let key = try nextOptionalKey() {
-        // Fast path:  We parsed it as UTF8 bytes...
-        try skipRequiredCharacter(asciiColon) // :
-        if let fieldNumber = names.number(forJSONName: key) {
-          return fieldNumber
-        }
-        if let s = utf8ToString(bytes: key.baseAddress!, count: key.count) {
-          fieldName = s
-        } else {
-          throw JSONDecodingError.invalidUTF8
-        }
-      } else {
-        // Slow path:  We parsed a String; lookups from String are slower.
-        fieldName = try nextQuotedString()
-        try skipRequiredCharacter(asciiColon) // :
-        if let fieldNumber = names.number(forJSONName: fieldName) {
-          return fieldNumber
-        }
-      }
-      if let first = fieldName.utf8.first, first == UInt8(ascii: "["),
-         let last = fieldName.utf8.last, last == UInt8(ascii: "]")
-      {
-        fieldName.removeFirst()
-        fieldName.removeLast()
-        if let fieldNumber = extensions.fieldNumberForProto(messageType: messageType, protoFieldName: fieldName) {
-          return fieldNumber
-        }
-      }
-      if !options.ignoreUnknownFields {
-        throw JSONDecodingError.unknownField(fieldName)
-      }
-      // Unknown field, skip it and try to parse the next field name
-      try skipValue()
-      if skipOptionalObjectEnd() {
-        return nil
-      }
-      try skipRequiredComma()
+    /// If the next non-whitespace character is "}", skip it
+    /// and return true.  Otherwise, return false.
+    internal mutating func skipOptionalObjectEnd() -> Bool {
+        let result = skipOptionalCharacter(asciiCloseCurlyBracket)  // }
+        if result {
+            decrementRecursionDepth()
+        }
+        return result
     }
-  }
 
-  /// Parse the next token as a string or numeric enum value.  Throws
-  /// unrecognizedEnumValue if the string/number can't initialize the
-  /// enum.  Will throw other errors if the JSON is malformed.
-  internal mutating func nextEnumValue<E: Enum>() throws -> E? {
-    func throwOrIgnore() throws -> E? {
-      if options.ignoreUnknownFields {
-        return nil
-      } else {
-        throw JSONDecodingError.unrecognizedEnumValue
-      }
-    }
-    skipWhitespace()
-    guard hasMoreContent else {
-        throw JSONDecodingError.truncated
-    }
-    if currentByte == asciiDoubleQuote {
-      if let name = try nextOptionalKey() {
-        if let e = E(rawUTF8: name) {
-          return e
+    /// Return the next complete JSON structure as a string.
+    /// For example, this might return "true", or "123.456",
+    /// or "{\"foo\": 7, \"bar\": [8, 9]}"
+    ///
+    /// Used by Any to get the upcoming JSON value as a string.
+    /// Note: The value might be an object or array.
+    internal mutating func skip() throws -> String {
+        skipWhitespace()
+        let start = index
+        try skipValue()
+        if let s = utf8ToString(bytes: source, start: start, end: index) {
+            return s
         } else {
-          return try throwOrIgnore()
-        }
-      }
-      let name = try nextQuotedString()
-      if let e = E(name: name) {
-        return e
-      } else {
-        return try throwOrIgnore()
-      }
-    } else {
-      let n = try nextSInt()
-      if let i = Int(exactly: n) {
-        if let e = E(rawValue: i) {
-          return e
-        } else {
-          return try throwOrIgnore()
+            throw JSONDecodingError.invalidUTF8
         }
-      } else {
-        throw JSONDecodingError.numberRange
-      }
-    }
-  }
-
-  /// Helper for skipping a single-character token.
-  private mutating func skipRequiredCharacter(_ required: UInt8) throws {
-    skipWhitespace()
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
-    }
-    let next = currentByte
-    if next == required {
-      advance()
-      return
-    }
-    throw JSONDecodingError.failure
-  }
-
-  /// Skip "{", throw if that's not the next character.
-  internal mutating func skipRequiredObjectStart() throws {
-    try skipRequiredCharacter(asciiOpenCurlyBracket) // {
-    try incrementRecursionDepth()
-  }
-
-  /// Skip ",", throw if that's not the next character.
-  internal mutating func skipRequiredComma() throws {
-    try skipRequiredCharacter(asciiComma)
-  }
-
-  /// Skip ":", throw if that's not the next character.
-  internal mutating func skipRequiredColon() throws {
-    try skipRequiredCharacter(asciiColon)
-  }
-
-  /// Skip "[", throw if that's not the next character.
-  internal mutating func skipRequiredArrayStart() throws {
-    try skipRequiredCharacter(asciiOpenSquareBracket) // [
-  }
-
-  /// Helper for skipping optional single-character tokens.
-  private mutating func skipOptionalCharacter(_ c: UInt8) -> Bool {
-    skipWhitespace()
-    if hasMoreContent && currentByte == c {
-      advance()
-      return true
-    }
-    return false
-  }
-
-  /// If the next non-whitespace character is "[", skip it
-  /// and return true.  Otherwise, return false.
-  internal mutating func skipOptionalArrayStart() -> Bool {
-    return skipOptionalCharacter(asciiOpenSquareBracket)
-  }
-
-  /// If the next non-whitespace character is "]", skip it
-  /// and return true.  Otherwise, return false.
-  internal mutating func skipOptionalArrayEnd() -> Bool {
-    return skipOptionalCharacter(asciiCloseSquareBracket) // ]
-  }
-
-  /// If the next non-whitespace character is "}", skip it
-  /// and return true.  Otherwise, return false.
-  internal mutating func skipOptionalObjectEnd() -> Bool {
-    let result = skipOptionalCharacter(asciiCloseCurlyBracket) // }
-    if result {
-      decrementRecursionDepth()
-    }
-    return result
-  }
-
-  /// Return the next complete JSON structure as a string.
-  /// For example, this might return "true", or "123.456",
-  /// or "{\"foo\": 7, \"bar\": [8, 9]}"
-  ///
-  /// Used by Any to get the upcoming JSON value as a string.
-  /// Note: The value might be an object or array.
-  internal mutating func skip() throws -> String {
-    skipWhitespace()
-    let start = index
-    try skipValue()
-    if let s = utf8ToString(bytes: source, start: start, end: index) {
-      return s
-    } else {
-      throw JSONDecodingError.invalidUTF8
     }
-  }
 
-  /// Advance index past the next value.  This is used
-  /// by skip() and by unknown field handling.
-  /// Note: This handles objects {...} recursively but arrays [...] non-recursively
-  /// This avoids us requiring excessive stack space for deeply nested
-  /// arrays (which are not included in the recursion budget check).
-  private mutating func skipValue() throws {
-    skipWhitespace()
-    var totalArrayDepth = 0
-    while true {
-        var arrayDepth = 0
-        while skipOptionalArrayStart() {
-            arrayDepth += 1
-        }
-        guard hasMoreContent else {
-          throw JSONDecodingError.truncated
-        }
-        switch currentByte {
-        case asciiDoubleQuote: // " begins a string
-            try skipString()
-        case asciiOpenCurlyBracket: // { begins an object
-            try skipObject()
-        case asciiCloseSquareBracket: // ] ends an empty array
-            if arrayDepth == 0 {
-                throw JSONDecodingError.failure
-            }
-            // We also close out [[]] or [[[]]] here
-            while arrayDepth > 0 && skipOptionalArrayEnd() {
-                arrayDepth -= 1
+    /// Advance index past the next value.  This is used
+    /// by skip() and by unknown field handling.
+    /// Note: This handles objects {...} recursively but arrays [...] non-recursively
+    /// This avoids us requiring excessive stack space for deeply nested
+    /// arrays (which are not included in the recursion budget check).
+    private mutating func skipValue() throws {
+        skipWhitespace()
+        var totalArrayDepth = 0
+        while true {
+            var arrayDepth = 0
+            while skipOptionalArrayStart() {
+                arrayDepth += 1
             }
-        case asciiLowerN: // n must be null
-            if !skipOptionalKeyword(bytes: [
-                asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL
-            ]) {
+            guard hasMoreContent else {
                 throw JSONDecodingError.truncated
             }
-        case asciiLowerF: // f must be false
-            if !skipOptionalKeyword(bytes: [
-                asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE
-            ]) {
-                throw JSONDecodingError.truncated
+            switch currentByte {
+            case asciiDoubleQuote:  // " begins a string
+                try skipString()
+            case asciiOpenCurlyBracket:  // { begins an object
+                try skipObject()
+            case asciiCloseSquareBracket:  // ] ends an empty array
+                if arrayDepth == 0 {
+                    throw JSONDecodingError.failure
+                }
+                // We also close out [[]] or [[[]]] here
+                while arrayDepth > 0 && skipOptionalArrayEnd() {
+                    arrayDepth -= 1
+                }
+            case asciiLowerN:  // n must be null
+                if !skipOptionalKeyword(bytes: [
+                    asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL,
+                ]) {
+                    throw JSONDecodingError.truncated
+                }
+            case asciiLowerF:  // f must be false
+                if !skipOptionalKeyword(bytes: [
+                    asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE,
+                ]) {
+                    throw JSONDecodingError.truncated
+                }
+            case asciiLowerT:  // t must be true
+                if !skipOptionalKeyword(bytes: [
+                    asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE,
+                ]) {
+                    throw JSONDecodingError.truncated
+                }
+            default:  // everything else is a number token
+                _ = try nextDouble()
             }
-        case asciiLowerT: // t must be true
-            if !skipOptionalKeyword(bytes: [
-                asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE
-            ]) {
-                throw JSONDecodingError.truncated
+            totalArrayDepth += arrayDepth
+            while totalArrayDepth > 0 && skipOptionalArrayEnd() {
+                totalArrayDepth -= 1
+            }
+            if totalArrayDepth > 0 {
+                try skipRequiredComma()
+            } else {
+                return
             }
-        default: // everything else is a number token
-            _ = try nextDouble()
         }
-        totalArrayDepth += arrayDepth
-        while totalArrayDepth > 0 && skipOptionalArrayEnd() {
-            totalArrayDepth -= 1
+    }
+
+    /// Advance the index past the next complete {...} construct.
+    private mutating func skipObject() throws {
+        try skipRequiredObjectStart()
+        if skipOptionalObjectEnd() {
+            return
         }
-        if totalArrayDepth > 0 {
+        while true {
+            skipWhitespace()
+            try skipString()
+            try skipRequiredColon()
+            try skipValue()
+            if skipOptionalObjectEnd() {
+                return
+            }
             try skipRequiredComma()
-        } else {
-            return
         }
     }
-  }
-
-  /// Advance the index past the next complete {...} construct.
-  private mutating func skipObject() throws {
-    try skipRequiredObjectStart()
-    if skipOptionalObjectEnd() {
-      return
-    }
-    while true {
-      skipWhitespace()
-      try skipString()
-      try skipRequiredColon()
-      try skipValue()
-      if skipOptionalObjectEnd() {
-        return
-      }
-      try skipRequiredComma()
-    }
-  }
 
-  /// Advance the index past the next complete quoted string.
-  ///
-  // Caveat:  This does not fully validate; it will accept
-  // strings that have malformed \ escapes.
-  //
-  // It would be nice to do better, but I don't think it's critical,
-  // since there are many reasons that strings (and other tokens for
-  // that matter) may be skippable but not parsable.  For example:
-  // Old clients that don't know new field types will skip fields
-  // they don't know; newer clients may reject the same input due to
-  // schema mismatches or other issues.
-  private mutating func skipString() throws {
-    guard hasMoreContent else {
-      throw JSONDecodingError.truncated
-    }
-    if currentByte != asciiDoubleQuote {
-      throw JSONDecodingError.malformedString
-    }
-    advance()
-    while hasMoreContent {
-      let c = currentByte
-      switch c {
-      case asciiDoubleQuote:
-        advance()
-        return
-      case asciiBackslash:
-        advance()
+    /// Advance the index past the next complete quoted string.
+    ///
+    // Caveat:  This does not fully validate; it will accept
+    // strings that have malformed \ escapes.
+    //
+    // It would be nice to do better, but I don't think it's critical,
+    // since there are many reasons that strings (and other tokens for
+    // that matter) may be skippable but not parsable.  For example:
+    // Old clients that don't know new field types will skip fields
+    // they don't know; newer clients may reject the same input due to
+    // schema mismatches or other issues.
+    private mutating func skipString() throws {
         guard hasMoreContent else {
-          throw JSONDecodingError.truncated
+            throw JSONDecodingError.truncated
+        }
+        if currentByte != asciiDoubleQuote {
+            throw JSONDecodingError.malformedString
         }
         advance()
-      default:
-        advance()
-      }
+        while hasMoreContent {
+            let c = currentByte
+            switch c {
+            case asciiDoubleQuote:
+                advance()
+                return
+            case asciiBackslash:
+                advance()
+                guard hasMoreContent else {
+                    throw JSONDecodingError.truncated
+                }
+                advance()
+            default:
+                advance()
+            }
+        }
+        throw JSONDecodingError.truncated
     }
-    throw JSONDecodingError.truncated
-  }
 }

+ 2 - 2
Sources/SwiftProtobuf/MathUtils.swift

@@ -21,7 +21,7 @@ import Foundation
 ///   - a: The dividend. Can be positive, 0 or negative.
 ///   - b: The divisor. This must be positive, and is an error if 0 or negative.
 /// - Returns: The unique value r such that 0 <= r < b and b * q + r = a for some q.
-internal func mod<T : SignedInteger>(_ a: T, _ b: T) -> T {
+internal func mod<T: SignedInteger>(_ a: T, _ b: T) -> T {
     assert(b > 0)
     let r = a % b
     return r >= 0 ? r : r + b
@@ -34,7 +34,7 @@ internal func mod<T : SignedInteger>(_ a: T, _ b: T) -> T {
 ///   - a: The dividend. Can be positive, 0 or negative.
 ///   - b: The divisor. This must be positive, and is an error if 0 or negative.
 /// - Returns: The unique value q such that for some 0 <= r < b, b * q + r = a.
-internal func div<T : SignedInteger>(_ a: T, _ b: T) -> T {
+internal func div<T: SignedInteger>(_ a: T, _ b: T) -> T {
     assert(b > 0)
     return a >= 0 ? a / b : (a + 1) / b - 1
 }

+ 29 - 29
Sources/SwiftProtobuf/Message+AnyAdditions.swift

@@ -13,33 +13,33 @@
 // -----------------------------------------------------------------------------
 
 extension Message {
-  /// Initialize this message from the provided `google.protobuf.Any`
-  /// well-known type.
-  ///
-  /// This corresponds to the `unpack` method in the Google C++ API.
-  ///
-  /// If the Any object was decoded from Protobuf Binary or JSON
-  /// format, then the enclosed field data was stored and is not
-  /// fully decoded until you unpack the Any object into a message.
-  /// As such, this method will typically need to perform a full
-  /// deserialization of the enclosed data and can fail for any
-  /// reason that deserialization can fail.
-  ///
-  /// See `Google_Protobuf_Any.unpackTo()` for more discussion.
-  ///
-  /// - Parameter unpackingAny: the message to decode.
-  /// - Parameter extensions: An `ExtensionMap` used to look up and decode any
-  ///   extensions in this message or messages nested within this message's
-  ///   fields.
-  /// - Parameter options: The BinaryDecodingOptions to use.
-  /// - Throws: an instance of ``AnyUnpackError``, ``JSONDecodingError``, or
-  ///   ``BinaryDecodingError`` on failure.
-  public init(
-    unpackingAny: Google_Protobuf_Any,
-    extensions: (any ExtensionMap)? = nil,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    self.init()
-    try unpackingAny._storage.unpackTo(target: &self, extensions: extensions, options: options)
-  }
+    /// Initialize this message from the provided `google.protobuf.Any`
+    /// well-known type.
+    ///
+    /// This corresponds to the `unpack` method in the Google C++ API.
+    ///
+    /// If the Any object was decoded from Protobuf Binary or JSON
+    /// format, then the enclosed field data was stored and is not
+    /// fully decoded until you unpack the Any object into a message.
+    /// As such, this method will typically need to perform a full
+    /// deserialization of the enclosed data and can fail for any
+    /// reason that deserialization can fail.
+    ///
+    /// See `Google_Protobuf_Any.unpackTo()` for more discussion.
+    ///
+    /// - Parameter unpackingAny: the message to decode.
+    /// - Parameter extensions: An `ExtensionMap` used to look up and decode any
+    ///   extensions in this message or messages nested within this message's
+    ///   fields.
+    /// - Parameter options: The BinaryDecodingOptions to use.
+    /// - Throws: an instance of ``AnyUnpackError``, ``JSONDecodingError``, or
+    ///   ``BinaryDecodingError`` on failure.
+    public init(
+        unpackingAny: Google_Protobuf_Any,
+        extensions: (any ExtensionMap)? = nil,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        self.init()
+        try unpackingAny._storage.unpackTo(target: &self, extensions: extensions, options: options)
+    }
 }

+ 129 - 127
Sources/SwiftProtobuf/Message+BinaryAdditions.swift

@@ -16,141 +16,143 @@ import Foundation
 
 /// Binary encoding and decoding methods for messages.
 extension Message {
-  /// Returns a ``SwiftProtobufContiguousBytes`` instance containing the Protocol Buffer binary
-  /// format serialization of the message.
-  ///
-  /// - Parameters:
-  ///   - partial: If `false` (the default), this method will check
-  ///     `Message.isInitialized` before encoding to verify that all required
-  ///     fields are present. If any are missing, this method throws.
-  ///     ``BinaryEncodingError/missingRequiredFields``.
-  ///   - options: The ``BinaryEncodingOptions`` to use.
-  /// - Returns: A ``SwiftProtobufContiguousBytes`` instance containing the binary serialization
-  /// of the message.
-  ///
-  /// - Throws: ``SwiftProtobufError`` or ``BinaryEncodingError`` if encoding fails.
-  public func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(
-    partial: Bool = false,
-    options: BinaryEncodingOptions = BinaryEncodingOptions()
-  ) throws -> Bytes {
-    if !partial && !isInitialized {
-      throw BinaryEncodingError.missingRequiredFields
-    }
+    /// Returns a ``SwiftProtobufContiguousBytes`` instance containing the Protocol Buffer binary
+    /// format serialization of the message.
+    ///
+    /// - Parameters:
+    ///   - partial: If `false` (the default), this method will check
+    ///     `Message.isInitialized` before encoding to verify that all required
+    ///     fields are present. If any are missing, this method throws.
+    ///     ``BinaryEncodingError/missingRequiredFields``.
+    ///   - options: The ``BinaryEncodingOptions`` to use.
+    /// - Returns: A ``SwiftProtobufContiguousBytes`` instance containing the binary serialization
+    /// of the message.
+    ///
+    /// - Throws: ``SwiftProtobufError`` or ``BinaryEncodingError`` if encoding fails.
+    public func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(
+        partial: Bool = false,
+        options: BinaryEncodingOptions = BinaryEncodingOptions()
+    ) throws -> Bytes {
+        if !partial && !isInitialized {
+            throw BinaryEncodingError.missingRequiredFields
+        }
 
-    // Note that this assumes `options` will not change the required size.
-    let requiredSize = try serializedDataSize()
+        // Note that this assumes `options` will not change the required size.
+        let requiredSize = try serializedDataSize()
 
-    // Messages have a 2GB limit in encoded size, the upstread C++ code
-    // (message_lite, etc.) does this enforcement also.
-    // https://protobuf.dev/programming-guides/encoding/#cheat-sheet
-    //
-    // Testing here enables the limit without adding extra conditionals to all
-    // the places that encode message fields (or strings/bytes fields), keeping
-    // the overhead of the check to a minimum.
-    guard requiredSize < 0x7fffffff else {
-      // Adding a new error is a breaking change.
-      throw BinaryEncodingError.missingRequiredFields
-    }
+        // Messages have a 2GB limit in encoded size, the upstread C++ code
+        // (message_lite, etc.) does this enforcement also.
+        // https://protobuf.dev/programming-guides/encoding/#cheat-sheet
+        //
+        // Testing here enables the limit without adding extra conditionals to all
+        // the places that encode message fields (or strings/bytes fields), keeping
+        // the overhead of the check to a minimum.
+        guard requiredSize < 0x7fff_ffff else {
+            // Adding a new error is a breaking change.
+            throw BinaryEncodingError.missingRequiredFields
+        }
 
-    var data = Bytes(repeating: 0, count: requiredSize)
-    try data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
-      var visitor = BinaryEncodingVisitor(forWritingInto: body, options: options)
-      try traverse(visitor: &visitor)
-      // Currently not exposing this from the api because it really would be
-      // an internal error in the library and should never happen.
-      assert(visitor.encoder.remainder.count == 0)
+        var data = Bytes(repeating: 0, count: requiredSize)
+        try data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
+            var visitor = BinaryEncodingVisitor(forWritingInto: body, options: options)
+            try traverse(visitor: &visitor)
+            // Currently not exposing this from the api because it really would be
+            // an internal error in the library and should never happen.
+            assert(visitor.encoder.remainder.count == 0)
+        }
+        return data
     }
-    return data
-  }
-
-  /// Returns the size in bytes required to encode the message in binary format.
-  /// This is used by `serializedData()` to precalculate the size of the buffer
-  /// so that encoding can proceed without bounds checks or reallocation.
-  internal func serializedDataSize() throws -> Int {
-    // Note: since this api is internal, it doesn't currently worry about
-    // needing a partial argument to handle required fields. If this become
-    // public, it will need that added.
-    var visitor = BinaryEncodingSizeVisitor()
-    try traverse(visitor: &visitor)
-    return visitor.serializedSize
-  }
 
-  /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` value
-  /// containing a serialized message in Protocol Buffer binary format.
-  ///
-  /// - Parameters:
-  ///   - serializedBytes: The binary-encoded message data to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryDecodingError/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``BinaryDecodingError`` if decoding fails.
-  @inlinable
-  public init<Bytes: SwiftProtobufContiguousBytes>(
-    serializedBytes bytes: Bytes,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    self.init()
-    try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options)
-  }
+    /// Returns the size in bytes required to encode the message in binary format.
+    /// This is used by `serializedData()` to precalculate the size of the buffer
+    /// so that encoding can proceed without bounds checks or reallocation.
+    internal func serializedDataSize() throws -> Int {
+        // Note: since this api is internal, it doesn't currently worry about
+        // needing a partial argument to handle required fields. If this become
+        // public, it will need that added.
+        var visitor = BinaryEncodingSizeVisitor()
+        try traverse(visitor: &visitor)
+        return visitor.serializedSize
+    }
 
-  /// Updates the message by decoding the given `SwiftProtobufContiguousBytes` value
-  /// containing a serialized message in Protocol Buffer binary format into the
-  /// receiver.
-  ///
-  /// - Note: If this method throws an error, the message may still have been
-  ///   partially mutated by the binary data that was decoded before the error
-  ///   occurred.
-  ///
-  /// - Parameters:
-  ///   - serializedBytes: The binary-encoded message data to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryDecodingError/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``BinaryDecodingError`` if decoding fails.
-  @inlinable
-  public mutating func merge<Bytes: SwiftProtobufContiguousBytes>(
-    serializedBytes bytes: Bytes,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-      try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options)
+    /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` value
+    /// containing a serialized message in Protocol Buffer binary format.
+    ///
+    /// - Parameters:
+    ///   - serializedBytes: The binary-encoded message data to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryDecodingError/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``BinaryDecodingError`` if decoding fails.
+    @inlinable
+    public init<Bytes: SwiftProtobufContiguousBytes>(
+        serializedBytes bytes: Bytes,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        self.init()
+        try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options)
     }
-  }
 
-  // Helper for `merge()`s to keep the Decoder internal to SwiftProtobuf while
-  // allowing the generic over `SwiftProtobufContiguousBytes` to get better codegen from the
-  // compiler by being `@inlinable`. For some discussion on this see
-  // https://github.com/apple/swift-protobuf/pull/914#issuecomment-555458153
-  @usableFromInline
-  internal mutating func _merge(
-    rawBuffer body: UnsafeRawBufferPointer,
-    extensions: (any ExtensionMap)?,
-    partial: Bool,
-    options: BinaryDecodingOptions
-  ) throws {
-    if let baseAddress = body.baseAddress, body.count > 0 {
-      var decoder = BinaryDecoder(forReadingFrom: baseAddress,
-                                  count: body.count,
-                                  options: options,
-                                  extensions: extensions)
-      try decoder.decodeFullMessage(message: &self)
+    /// Updates the message by decoding the given `SwiftProtobufContiguousBytes` value
+    /// containing a serialized message in Protocol Buffer binary format into the
+    /// receiver.
+    ///
+    /// - Note: If this method throws an error, the message may still have been
+    ///   partially mutated by the binary data that was decoded before the error
+    ///   occurred.
+    ///
+    /// - Parameters:
+    ///   - serializedBytes: The binary-encoded message data to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryDecodingError/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``BinaryDecodingError`` if decoding fails.
+    @inlinable
+    public mutating func merge<Bytes: SwiftProtobufContiguousBytes>(
+        serializedBytes bytes: Bytes,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+            try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options)
+        }
     }
-    if !partial && !isInitialized {
-      throw BinaryDecodingError.missingRequiredFields
+
+    // Helper for `merge()`s to keep the Decoder internal to SwiftProtobuf while
+    // allowing the generic over `SwiftProtobufContiguousBytes` to get better codegen from the
+    // compiler by being `@inlinable`. For some discussion on this see
+    // https://github.com/apple/swift-protobuf/pull/914#issuecomment-555458153
+    @usableFromInline
+    internal mutating func _merge(
+        rawBuffer body: UnsafeRawBufferPointer,
+        extensions: (any ExtensionMap)?,
+        partial: Bool,
+        options: BinaryDecodingOptions
+    ) throws {
+        if let baseAddress = body.baseAddress, body.count > 0 {
+            var decoder = BinaryDecoder(
+                forReadingFrom: baseAddress,
+                count: body.count,
+                options: options,
+                extensions: extensions
+            )
+            try decoder.decodeFullMessage(message: &self)
+        }
+        if !partial && !isInitialized {
+            throw BinaryDecodingError.missingRequiredFields
+        }
     }
-  }
 }

+ 207 - 197
Sources/SwiftProtobuf/Message+BinaryAdditions_Data.swift

@@ -16,210 +16,220 @@ import Foundation
 
 /// Binary encoding and decoding methods for messages.
 extension Message {
-  /// Creates a new message by decoding the given `Data` value
-  /// containing a serialized message in Protocol Buffer binary format.
-  ///
-  /// - Parameters:
-  ///   - serializedData: The binary-encoded message `Data` to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryDecodingError/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``BinaryDecodingError`` if decoding fails.
-  @inlinable
-  @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)")
-  public init(
-    serializedData data: Data,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    self.init()
-    try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options)
-  }
+    /// Creates a new message by decoding the given `Data` value
+    /// containing a serialized message in Protocol Buffer binary format.
+    ///
+    /// - Parameters:
+    ///   - serializedData: The binary-encoded message `Data` to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryDecodingError/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``BinaryDecodingError`` if decoding fails.
+    @inlinable
+    @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)")
+    public init(
+        serializedData data: Data,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        self.init()
+        try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options)
+    }
 
-  /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value
-  /// containing a serialized message in Protocol Buffer binary format.
-  ///
-  /// - Parameters:
-  ///   - contiguousBytes: The binary-encoded message data to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``SwiftProtobufError`` if decoding fails.
-  @inlinable
-  @_disfavoredOverload
-  @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)")
-  public init<Bytes: ContiguousBytes>(
-    contiguousBytes bytes: Bytes,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    self.init()
-    try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options)
-  }
+    /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value
+    /// containing a serialized message in Protocol Buffer binary format.
+    ///
+    /// - Parameters:
+    ///   - contiguousBytes: The binary-encoded message data to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``SwiftProtobufError`` if decoding fails.
+    @inlinable
+    @_disfavoredOverload
+    @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)")
+    public init<Bytes: ContiguousBytes>(
+        contiguousBytes bytes: Bytes,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        self.init()
+        try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options)
+    }
 
-  /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value
-  /// containing a serialized message in Protocol Buffer binary format.
-  ///
-  /// - Parameters:
-  ///   - serializedBytes: The binary-encoded message data to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``SwiftProtobufError`` if decoding fails.
-  @inlinable
-  @_disfavoredOverload
-  @available(*, deprecated, message: "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`.")
-  public init<Bytes: ContiguousBytes>(
-    serializedBytes bytes: Bytes,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    self.init()
-    try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options)
-  }
+    /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value
+    /// containing a serialized message in Protocol Buffer binary format.
+    ///
+    /// - Parameters:
+    ///   - serializedBytes: The binary-encoded message data to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``SwiftProtobufError`` if decoding fails.
+    @inlinable
+    @_disfavoredOverload
+    @available(
+        *,
+        deprecated,
+        message:
+            "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`."
+    )
+    public init<Bytes: ContiguousBytes>(
+        serializedBytes bytes: Bytes,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        self.init()
+        try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options)
+    }
 
-  /// Updates the message by decoding the given `Foundation/ContiguousBytes` value
-  /// containing a serialized message in Protocol Buffer binary format into the
-  /// receiver.
-  ///
-  /// - Note: If this method throws an error, the message may still have been
-  ///   partially mutated by the binary data that was decoded before the error
-  ///   occurred.
-  ///
-  /// - Parameters:
-  ///   - contiguousBytes: The binary-encoded message data to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``SwiftProtobufError`` if decoding fails.
-  @inlinable
-  @_disfavoredOverload
-  @available(*, deprecated, renamed: "merge(serializedBytes:extensions:partial:options:)")
-  public mutating func merge<Bytes: ContiguousBytes>(
-    contiguousBytes bytes: Bytes,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-      try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options)
+    /// Updates the message by decoding the given `Foundation/ContiguousBytes` value
+    /// containing a serialized message in Protocol Buffer binary format into the
+    /// receiver.
+    ///
+    /// - Note: If this method throws an error, the message may still have been
+    ///   partially mutated by the binary data that was decoded before the error
+    ///   occurred.
+    ///
+    /// - Parameters:
+    ///   - contiguousBytes: The binary-encoded message data to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``SwiftProtobufError`` if decoding fails.
+    @inlinable
+    @_disfavoredOverload
+    @available(*, deprecated, renamed: "merge(serializedBytes:extensions:partial:options:)")
+    public mutating func merge<Bytes: ContiguousBytes>(
+        contiguousBytes bytes: Bytes,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+            try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options)
+        }
     }
-  }
 
-  /// Updates the message by decoding the given `Foundation/ContiguousBytes` value
-  /// containing a serialized message in Protocol Buffer binary format into the
-  /// receiver.
-  ///
-  /// - Note: If this method throws an error, the message may still have been
-  ///   partially mutated by the binary data that was decoded before the error
-  ///   occurred.
-  ///
-  /// - Parameters:
-  ///   - serializedBytes: The binary-encoded message data to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``SwiftProtobufError`` if decoding fails.
-  @inlinable
-  @_disfavoredOverload
-  @available(*, deprecated, message: "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`.")
-  public mutating func merge<Bytes: ContiguousBytes>(
-    serializedBytes bytes: Bytes,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-      try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options)
+    /// Updates the message by decoding the given `Foundation/ContiguousBytes` value
+    /// containing a serialized message in Protocol Buffer binary format into the
+    /// receiver.
+    ///
+    /// - Note: If this method throws an error, the message may still have been
+    ///   partially mutated by the binary data that was decoded before the error
+    ///   occurred.
+    ///
+    /// - Parameters:
+    ///   - serializedBytes: The binary-encoded message data to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``SwiftProtobufError`` if decoding fails.
+    @inlinable
+    @_disfavoredOverload
+    @available(
+        *,
+        deprecated,
+        message:
+            "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`."
+    )
+    public mutating func merge<Bytes: ContiguousBytes>(
+        serializedBytes bytes: Bytes,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+            try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options)
+        }
     }
-  }
 
-  /// Updates the message by decoding the given `Data` value
-  /// containing a serialized message in Protocol Buffer binary format into the
-  /// receiver.
-  ///
-  /// - Note: If this method throws an error, the message may still have been
-  ///   partially mutated by the binary data that was decoded before the error
-  ///   occurred.
-  ///
-  /// - Parameters:
-  ///   - serializedData: The binary-encoded message data to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryDecodingError/missingRequiredFields``.
-  ///   - options: The ``BinaryDecodingOptions`` to use.
-  /// - Throws: ``BinaryDecodingError`` if decoding fails.
-  @inlinable
-  public mutating func merge(
-    serializedData data: Data,
-    extensions: (any ExtensionMap)? = nil,
-    partial: Bool = false,
-    options: BinaryDecodingOptions = BinaryDecodingOptions()
-  ) throws {
-    try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options)
-  }
+    /// Updates the message by decoding the given `Data` value
+    /// containing a serialized message in Protocol Buffer binary format into the
+    /// receiver.
+    ///
+    /// - Note: If this method throws an error, the message may still have been
+    ///   partially mutated by the binary data that was decoded before the error
+    ///   occurred.
+    ///
+    /// - Parameters:
+    ///   - serializedData: The binary-encoded message data to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` after decoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryDecodingError/missingRequiredFields``.
+    ///   - options: The ``BinaryDecodingOptions`` to use.
+    /// - Throws: ``BinaryDecodingError`` if decoding fails.
+    @inlinable
+    public mutating func merge(
+        serializedData data: Data,
+        extensions: (any ExtensionMap)? = nil,
+        partial: Bool = false,
+        options: BinaryDecodingOptions = BinaryDecodingOptions()
+    ) throws {
+        try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options)
+    }
 
-  /// Returns a `Data` instance containing the Protocol Buffer binary
-  /// format serialization of the message.
-  ///
-  /// - Parameters:
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryEncodingError/missingRequiredFields``.
-  /// - Returns: A `Data` instance containing the binary serialization of the message.
-  /// - Throws: ``BinaryEncodingError`` if encoding fails.
-  public func serializedData(partial: Bool = false) throws -> Data {
-    try serializedBytes(partial: partial, options: BinaryEncodingOptions())
-  }
+    /// Returns a `Data` instance containing the Protocol Buffer binary
+    /// format serialization of the message.
+    ///
+    /// - Parameters:
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryEncodingError/missingRequiredFields``.
+    /// - Returns: A `Data` instance containing the binary serialization of the message.
+    /// - Throws: ``BinaryEncodingError`` if encoding fails.
+    public func serializedData(partial: Bool = false) throws -> Data {
+        try serializedBytes(partial: partial, options: BinaryEncodingOptions())
+    }
 
-  /// Returns a `Data` instance containing the Protocol Buffer binary
-  /// format serialization of the message.
-  ///
-  /// - Parameters:
-  ///   - partial: If `false` (the default), this method will check
-  ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
-  ///     fields are present. If any are missing, this method throws
-  ///     ``BinaryEncodingError/missingRequiredFields``.
-  ///   - options: The ``BinaryEncodingOptions`` to use.
-  /// - Returns: A `Data` instance containing the binary serialization of the message.
-  /// - Throws: ``BinaryEncodingError`` if encoding fails.
-  public func serializedData(
-    partial: Bool = false, 
-    options: BinaryEncodingOptions = BinaryEncodingOptions()
-  ) throws -> Data {
-    try serializedBytes(partial: partial, options: options)
-  }
+    /// Returns a `Data` instance containing the Protocol Buffer binary
+    /// format serialization of the message.
+    ///
+    /// - Parameters:
+    ///   - partial: If `false` (the default), this method will check
+    ///     ``Message/isInitialized-6abgi`` before encoding to verify that all required
+    ///     fields are present. If any are missing, this method throws
+    ///     ``BinaryEncodingError/missingRequiredFields``.
+    ///   - options: The ``BinaryEncodingOptions`` to use.
+    /// - Returns: A `Data` instance containing the binary serialization of the message.
+    /// - Throws: ``BinaryEncodingError`` if encoding fails.
+    public func serializedData(
+        partial: Bool = false,
+        options: BinaryEncodingOptions = BinaryEncodingOptions()
+    ) throws -> Data {
+        try serializedBytes(partial: partial, options: options)
+    }
 }

+ 91 - 90
Sources/SwiftProtobuf/Message+FieldMask.swift

@@ -16,117 +16,118 @@ import Foundation
 
 extension Message {
 
-  /// Checks whether the given path is valid for Message type.
-  ///
-  /// - Parameter path: Path to be checked
-  /// - Returns: Boolean determines path is valid.
-  public static func isPathValid(
-    _ path: String
-  ) -> Bool {
-    var message = Self()
-    return message.hasPath(path: path)
-  }
+    /// Checks whether the given path is valid for Message type.
+    ///
+    /// - Parameter path: Path to be checked
+    /// - Returns: Boolean determines path is valid.
+    public static func isPathValid(
+        _ path: String
+    ) -> Bool {
+        var message = Self()
+        return message.hasPath(path: path)
+    }
 
-  internal mutating func hasPath(path: String) -> Bool {
-    do {
-      try set(path: path, value: nil, mergeOption: .init())
-      return true
-    } catch let error as PathDecodingError {
-      return error != .pathNotFound
-    } catch {
-      return false
+    internal mutating func hasPath(path: String) -> Bool {
+        do {
+            try set(path: path, value: nil, mergeOption: .init())
+            return true
+        } catch let error as PathDecodingError {
+            return error != .pathNotFound
+        } catch {
+            return false
+        }
     }
-  }
 
-  internal mutating func isPathValid(
-    _ path: String
-  ) -> Bool {
-    hasPath(path: path)
-  }
+    internal mutating func isPathValid(
+        _ path: String
+    ) -> Bool {
+        hasPath(path: path)
+    }
 }
 
 extension Google_Protobuf_FieldMask {
 
-  /// Defines available options for merging two messages.
-  public struct MergeOptions {
+    /// Defines available options for merging two messages.
+    public struct MergeOptions {
 
-    public init() {}
+        public init() {}
 
-    /// The default merging behavior will append entries from the source
-    /// repeated field to the destination repeated field. If you only want
-    /// to keep the entries from the source repeated field, set this flag
-    /// to true.
-    public var replaceRepeatedFields = false
-  }
+        /// The default merging behavior will append entries from the source
+        /// repeated field to the destination repeated field. If you only want
+        /// to keep the entries from the source repeated field, set this flag
+        /// to true.
+        public var replaceRepeatedFields = false
+    }
 }
 
 extension Message {
 
-  /// Merges fields specified in a FieldMask into another message.
-  ///
-  /// - Parameters:
-  ///   - source: Message that should be merged to the original one.
-  ///   - fieldMask: FieldMask specifies which fields should be merged.
-  public mutating func merge(
-    from source: Self,
-    fieldMask: Google_Protobuf_FieldMask,
-    mergeOption: Google_Protobuf_FieldMask.MergeOptions = .init()
-  ) throws {
-    var visitor = PathVisitor<Self>()
-    try source.traverse(visitor: &visitor)
-    let values = visitor.values
-    // TODO: setting all values with only one decoding
-    for path in fieldMask.paths {
-      try? set(
-        path: path,
-        value: values[path],
-        mergeOption: mergeOption
-      )
+    /// Merges fields specified in a FieldMask into another message.
+    ///
+    /// - Parameters:
+    ///   - source: Message that should be merged to the original one.
+    ///   - fieldMask: FieldMask specifies which fields should be merged.
+    public mutating func merge(
+        from source: Self,
+        fieldMask: Google_Protobuf_FieldMask,
+        mergeOption: Google_Protobuf_FieldMask.MergeOptions = .init()
+    ) throws {
+        var visitor = PathVisitor<Self>()
+        try source.traverse(visitor: &visitor)
+        let values = visitor.values
+        // TODO: setting all values with only one decoding
+        for path in fieldMask.paths {
+            try? set(
+                path: path,
+                value: values[path],
+                mergeOption: mergeOption
+            )
+        }
     }
-  }
 }
 
 extension Message where Self: Equatable, Self: _ProtoNameProviding {
 
-  // TODO: Re-implement using clear fields instead of copying message
+    // TODO: Re-implement using clear fields instead of copying message
 
-  /// Removes from 'message' any field that is not represented in the given
-  /// FieldMask. If the FieldMask is empty, does nothing.
-  ///
-  /// - Parameter fieldMask: FieldMask specifies which fields should be kept.
-  /// - Returns: Boolean determines if the message is modified
-  @discardableResult
-  public mutating func trim(
-    keeping fieldMask: Google_Protobuf_FieldMask
-  ) -> Bool {
-    if !fieldMask.isValid(for: Self.self) {
-      return false
+    /// Removes from 'message' any field that is not represented in the given
+    /// FieldMask. If the FieldMask is empty, does nothing.
+    ///
+    /// - Parameter fieldMask: FieldMask specifies which fields should be kept.
+    /// - Returns: Boolean determines if the message is modified
+    @discardableResult
+    public mutating func trim(
+        keeping fieldMask: Google_Protobuf_FieldMask
+    ) -> Bool {
+        if !fieldMask.isValid(for: Self.self) {
+            return false
+        }
+        if fieldMask.paths.isEmpty {
+            return false
+        }
+        var tmp = Self(removingAllFieldsOf: self)
+        do {
+            try tmp.merge(from: self, fieldMask: fieldMask)
+            let changed = tmp != self
+            self = tmp
+            return changed
+        } catch {
+            return false
+        }
     }
-    if fieldMask.paths.isEmpty {
-      return false
-    }
-    var tmp = Self(removingAllFieldsOf: self)
-    do {
-      try tmp.merge(from: self, fieldMask: fieldMask)
-      let changed = tmp != self
-      self = tmp
-      return changed
-    } catch {
-      return false
-    }
-  }
 }
 
-private extension Message {
-  init(removingAllFieldsOf message: Self) {
-    let newMessage: Self = .init()
-    if var newExtensible = newMessage as? any ExtensibleMessage,
-       let extensible = message as? any ExtensibleMessage {
-      newExtensible._protobuf_extensionFieldValues = extensible._protobuf_extensionFieldValues
-      self = newExtensible as? Self ?? newMessage
-    } else {
-      self = newMessage
+extension Message {
+    fileprivate init(removingAllFieldsOf message: Self) {
+        let newMessage: Self = .init()
+        if var newExtensible = newMessage as? any ExtensibleMessage,
+            let extensible = message as? any ExtensibleMessage
+        {
+            newExtensible._protobuf_extensionFieldValues = extensible._protobuf_extensionFieldValues
+            self = newExtensible as? Self ?? newMessage
+        } else {
+            self = newMessage
+        }
+        self.unknownFields = message.unknownFields
     }
-    self.unknownFields = message.unknownFields
-  }
 }

+ 124 - 120
Sources/SwiftProtobuf/Message+JSONAdditions.swift

@@ -16,134 +16,138 @@ import Foundation
 
 /// JSON encoding and decoding methods for messages.
 extension Message {
-  /// Returns a string containing the JSON serialization of the message.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to JSON.
-  ///
-  /// - Returns: A string containing the JSON serialization of the message.
-  /// - Parameters:
-  ///   - options: The JSONEncodingOptions to use.
-  /// - Throws: ``JSONEncodingError`` if encoding fails.
-  public func jsonString(
-    options: JSONEncodingOptions = JSONEncodingOptions()
-  ) throws -> String {
-    if let m = self as? (any _CustomJSONCodable) {
-      return try m.encodedJSONString(options: options)
+    /// Returns a string containing the JSON serialization of the message.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to JSON.
+    ///
+    /// - Returns: A string containing the JSON serialization of the message.
+    /// - Parameters:
+    ///   - options: The JSONEncodingOptions to use.
+    /// - Throws: ``JSONEncodingError`` if encoding fails.
+    public func jsonString(
+        options: JSONEncodingOptions = JSONEncodingOptions()
+    ) throws -> String {
+        if let m = self as? (any _CustomJSONCodable) {
+            return try m.encodedJSONString(options: options)
+        }
+        let data: [UInt8] = try jsonUTF8Bytes(options: options)
+        return String(bytes: data, encoding: .utf8)!
     }
-    let data: [UInt8] = try jsonUTF8Bytes(options: options)
-    return String(bytes: data, encoding: .utf8)!
-  }
 
-  /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the message.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to JSON.
-  ///
-  /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the message.
-  /// - Parameters:
-  ///   - options: The JSONEncodingOptions to use.
-  /// - Throws: ``JSONEncodingError`` if encoding fails.
-  public func jsonUTF8Bytes<Bytes: SwiftProtobufContiguousBytes>(
-    options: JSONEncodingOptions = JSONEncodingOptions()
-  ) throws -> Bytes {
-    if let m = self as? (any _CustomJSONCodable) {
-      let string = try m.encodedJSONString(options: options)
-      return Bytes(string.utf8)
+    /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the message.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to JSON.
+    ///
+    /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the message.
+    /// - Parameters:
+    ///   - options: The JSONEncodingOptions to use.
+    /// - Throws: ``JSONEncodingError`` if encoding fails.
+    public func jsonUTF8Bytes<Bytes: SwiftProtobufContiguousBytes>(
+        options: JSONEncodingOptions = JSONEncodingOptions()
+    ) throws -> Bytes {
+        if let m = self as? (any _CustomJSONCodable) {
+            let string = try m.encodedJSONString(options: options)
+            return Bytes(string.utf8)
+        }
+        var visitor = try JSONEncodingVisitor(type: Self.self, options: options)
+        visitor.startObject(message: self)
+        try traverse(visitor: &visitor)
+        visitor.endObject()
+        return Bytes(visitor.dataResult)
     }
-    var visitor = try JSONEncodingVisitor(type: Self.self, options: options)
-    visitor.startObject(message: self)
-    try traverse(visitor: &visitor)
-    visitor.endObject()
-    return Bytes(visitor.dataResult)
-  }
-
-  /// Creates a new message by decoding the given string containing a
-  /// serialized message in JSON format.
-  ///
-  /// - Parameter jsonString: The JSON-formatted string to decode.
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public init(
-    jsonString: String,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws {
-    try self.init(jsonString: jsonString, extensions: nil, options: options)
-  }
 
-  /// Creates a new message by decoding the given string containing a
-  /// serialized message in JSON format.
-  ///
-  /// - Parameter jsonString: The JSON-formatted string to decode.
-  /// - Parameter extensions: An ExtensionMap for looking up extensions by name
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public init(
-    jsonString: String,
-    extensions: (any ExtensionMap)? = nil,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws {
-    if jsonString.isEmpty {
-      throw JSONDecodingError.truncated
+    /// Creates a new message by decoding the given string containing a
+    /// serialized message in JSON format.
+    ///
+    /// - Parameter jsonString: The JSON-formatted string to decode.
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public init(
+        jsonString: String,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws {
+        try self.init(jsonString: jsonString, extensions: nil, options: options)
     }
-    if let data = jsonString.data(using: String.Encoding.utf8) {
-      try self.init(jsonUTF8Bytes: data, extensions: extensions, options: options)
-    } else {
-      throw JSONDecodingError.truncated
+
+    /// Creates a new message by decoding the given string containing a
+    /// serialized message in JSON format.
+    ///
+    /// - Parameter jsonString: The JSON-formatted string to decode.
+    /// - Parameter extensions: An ExtensionMap for looking up extensions by name
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public init(
+        jsonString: String,
+        extensions: (any ExtensionMap)? = nil,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws {
+        if jsonString.isEmpty {
+            throw JSONDecodingError.truncated
+        }
+        if let data = jsonString.data(using: String.Encoding.utf8) {
+            try self.init(jsonUTF8Bytes: data, extensions: extensions, options: options)
+        } else {
+            throw JSONDecodingError.truncated
+        }
     }
-  }
 
-  /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes`
-  /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded
-  /// text.
-  ///
-  /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public init<Bytes: SwiftProtobufContiguousBytes>(
-    jsonUTF8Bytes: Bytes,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws {
-    try self.init(jsonUTF8Bytes: jsonUTF8Bytes, extensions: nil, options: options)
-  }
+    /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes`
+    /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded
+    /// text.
+    ///
+    /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public init<Bytes: SwiftProtobufContiguousBytes>(
+        jsonUTF8Bytes: Bytes,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws {
+        try self.init(jsonUTF8Bytes: jsonUTF8Bytes, extensions: nil, options: options)
+    }
 
-  /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes`
-  /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded
-  /// text.
-  ///
-  /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter extensions: The extension map to use with this decode
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public init<Bytes: SwiftProtobufContiguousBytes>(
-    jsonUTF8Bytes: Bytes,
-    extensions: (any ExtensionMap)? = nil,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws {
-    self.init()
-    try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-      // Empty input is valid for binary, but not for JSON.
-      guard body.count > 0 else {
-        throw JSONDecodingError.truncated
-      }
-      var decoder = JSONDecoder(source: body, options: options,
-                                messageType: Self.self, extensions: extensions)
-      if decoder.scanner.skipOptionalNull() {
-        if let customCodable = Self.self as? any _CustomJSONCodable.Type,
-           let message = try customCodable.decodedFromJSONNull() {
-          self = message as! Self
-        } else {
-          throw JSONDecodingError.illegalNull
+    /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes`
+    /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded
+    /// text.
+    ///
+    /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter extensions: The extension map to use with this decode
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public init<Bytes: SwiftProtobufContiguousBytes>(
+        jsonUTF8Bytes: Bytes,
+        extensions: (any ExtensionMap)? = nil,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws {
+        self.init()
+        try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+            // Empty input is valid for binary, but not for JSON.
+            guard body.count > 0 else {
+                throw JSONDecodingError.truncated
+            }
+            var decoder = JSONDecoder(
+                source: body,
+                options: options,
+                messageType: Self.self,
+                extensions: extensions
+            )
+            if decoder.scanner.skipOptionalNull() {
+                if let customCodable = Self.self as? any _CustomJSONCodable.Type,
+                    let message = try customCodable.decodedFromJSONNull()
+                {
+                    self = message as! Self
+                } else {
+                    throw JSONDecodingError.illegalNull
+                }
+            } else {
+                try decoder.decodeFullObject(message: &self)
+            }
+            if !decoder.scanner.complete {
+                throw JSONDecodingError.trailingGarbage
+            }
         }
-      } else {
-        try decoder.decodeFullObject(message: &self)
-      }
-      if !decoder.scanner.complete {
-        throw JSONDecodingError.trailingGarbage
-      }
     }
-  }
 }
-

+ 42 - 43
Sources/SwiftProtobuf/Message+JSONAdditions_Data.swift

@@ -16,49 +16,48 @@ import Foundation
 
 /// JSON encoding and decoding methods for messages.
 extension Message {
-  /// Creates a new message by decoding the given `Data` containing a serialized message
-  /// in JSON format, interpreting the data as UTF-8 encoded text.
-  ///
-  /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public init(
-    jsonUTF8Data: Data,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws {
-    try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: nil, options: options)
-  }
+    /// Creates a new message by decoding the given `Data` containing a serialized message
+    /// in JSON format, interpreting the data as UTF-8 encoded text.
+    ///
+    /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public init(
+        jsonUTF8Data: Data,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws {
+        try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: nil, options: options)
+    }
 
-  /// Creates a new message by decoding the given `Data` containing a serialized message
-  /// in JSON format, interpreting the data as UTF-8 encoded text.
-  ///
-  /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter extensions: The extension map to use with this decode
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public init(
-    jsonUTF8Data: Data,
-    extensions: (any ExtensionMap)? = nil,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws {
-    try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: extensions, options: options)
-  }
+    /// Creates a new message by decoding the given `Data` containing a serialized message
+    /// in JSON format, interpreting the data as UTF-8 encoded text.
+    ///
+    /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter extensions: The extension map to use with this decode
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public init(
+        jsonUTF8Data: Data,
+        extensions: (any ExtensionMap)? = nil,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws {
+        try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: extensions, options: options)
+    }
 
-  /// Returns a Data containing the UTF-8 JSON serialization of the message.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to JSON.
-  ///
-  /// - Returns: A Data containing the JSON serialization of the message.
-  /// - Parameters:
-  ///   - options: The JSONEncodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if encoding fails.
-  public func jsonUTF8Data(
-    options: JSONEncodingOptions = JSONEncodingOptions()
-  ) throws -> Data {
-    try jsonUTF8Bytes(options: options)
-  }
+    /// Returns a Data containing the UTF-8 JSON serialization of the message.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to JSON.
+    ///
+    /// - Returns: A Data containing the JSON serialization of the message.
+    /// - Parameters:
+    ///   - options: The JSONEncodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if encoding fails.
+    public func jsonUTF8Data(
+        options: JSONEncodingOptions = JSONEncodingOptions()
+    ) throws -> Data {
+        try jsonUTF8Bytes(options: options)
+    }
 }
-

+ 123 - 115
Sources/SwiftProtobuf/Message+JSONArrayAdditions.swift

@@ -16,131 +16,139 @@ import Foundation
 
 /// JSON encoding and decoding methods for arrays of messages.
 extension Message {
-  /// Returns a string containing the JSON serialization of the messages.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to JSON.
-  ///
-  /// - Returns: A string containing the JSON serialization of the messages.
-  /// - Parameters:
-  ///   - collection: The list of messages to encode.
-  ///   - options: The JSONEncodingOptions to use.
-  /// - Throws: ``JSONEncodingError`` if encoding fails.
-  public static func jsonString<C: Collection>(
-    from collection: C,
-    options: JSONEncodingOptions = JSONEncodingOptions()
-  ) throws -> String where C.Iterator.Element == Self {
-    let data: [UInt8] = try jsonUTF8Bytes(from: collection, options: options)
-    return String(bytes: data, encoding: .utf8)!
-  }
+    /// Returns a string containing the JSON serialization of the messages.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to JSON.
+    ///
+    /// - Returns: A string containing the JSON serialization of the messages.
+    /// - Parameters:
+    ///   - collection: The list of messages to encode.
+    ///   - options: The JSONEncodingOptions to use.
+    /// - Throws: ``JSONEncodingError`` if encoding fails.
+    public static func jsonString<C: Collection>(
+        from collection: C,
+        options: JSONEncodingOptions = JSONEncodingOptions()
+    ) throws -> String where C.Iterator.Element == Self {
+        let data: [UInt8] = try jsonUTF8Bytes(from: collection, options: options)
+        return String(bytes: data, encoding: .utf8)!
+    }
 
-  /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the messages.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to JSON.
-  ///
-  /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the messages.
-  /// - Parameters:
-  ///   - collection: The list of messages to encode.
-  ///   - options: The JSONEncodingOptions to use.
-  /// - Throws: ``JSONEncodingError`` if encoding fails.
-  public static func jsonUTF8Bytes<C: Collection, Bytes: SwiftProtobufContiguousBytes>(
-    from collection: C,
-    options: JSONEncodingOptions = JSONEncodingOptions()
-  ) throws -> Bytes where C.Iterator.Element == Self {
-    var visitor = try JSONEncodingVisitor(type: Self.self, options: options)
-    visitor.startArray()
-    for message in collection {
-        visitor.startArrayObject(message: message)
-        try message.traverse(visitor: &visitor)
-        visitor.endObject()
+    /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the messages.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to JSON.
+    ///
+    /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the messages.
+    /// - Parameters:
+    ///   - collection: The list of messages to encode.
+    ///   - options: The JSONEncodingOptions to use.
+    /// - Throws: ``JSONEncodingError`` if encoding fails.
+    public static func jsonUTF8Bytes<C: Collection, Bytes: SwiftProtobufContiguousBytes>(
+        from collection: C,
+        options: JSONEncodingOptions = JSONEncodingOptions()
+    ) throws -> Bytes where C.Iterator.Element == Self {
+        var visitor = try JSONEncodingVisitor(type: Self.self, options: options)
+        visitor.startArray()
+        for message in collection {
+            visitor.startArrayObject(message: message)
+            try message.traverse(visitor: &visitor)
+            visitor.endObject()
+        }
+        visitor.endArray()
+        return Bytes(visitor.dataResult)
     }
-    visitor.endArray()
-    return Bytes(visitor.dataResult)
-  }
 
-  /// Creates a new array of messages by decoding the given string containing a
-  /// serialized array of messages in JSON format.
-  ///
-  /// - Parameter jsonString: The JSON-formatted string to decode.
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public static func array(
-    fromJSONString jsonString: String,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws -> [Self] {
-    return try self.array(fromJSONString: jsonString,
-                          extensions: SimpleExtensionMap(),
-                          options: options)
-  }
+    /// Creates a new array of messages by decoding the given string containing a
+    /// serialized array of messages in JSON format.
+    ///
+    /// - Parameter jsonString: The JSON-formatted string to decode.
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public static func array(
+        fromJSONString jsonString: String,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws -> [Self] {
+        try self.array(
+            fromJSONString: jsonString,
+            extensions: SimpleExtensionMap(),
+            options: options
+        )
+    }
 
-  /// Creates a new array of messages by decoding the given string containing a
-  /// serialized array of messages in JSON format.
-  ///
-  /// - Parameter jsonString: The JSON-formatted string to decode.
-  /// - Parameter extensions: The extension map to use with this decode
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public static func array(
-    fromJSONString jsonString: String,
-    extensions: any ExtensionMap = SimpleExtensionMap(),
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws -> [Self] {
-    if jsonString.isEmpty {
-      throw JSONDecodingError.truncated
+    /// Creates a new array of messages by decoding the given string containing a
+    /// serialized array of messages in JSON format.
+    ///
+    /// - Parameter jsonString: The JSON-formatted string to decode.
+    /// - Parameter extensions: The extension map to use with this decode
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public static func array(
+        fromJSONString jsonString: String,
+        extensions: any ExtensionMap = SimpleExtensionMap(),
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws -> [Self] {
+        if jsonString.isEmpty {
+            throw JSONDecodingError.truncated
+        }
+        if let data = jsonString.data(using: String.Encoding.utf8) {
+            return try array(fromJSONUTF8Bytes: data, extensions: extensions, options: options)
+        } else {
+            throw JSONDecodingError.truncated
+        }
     }
-    if let data = jsonString.data(using: String.Encoding.utf8) {
-      return try array(fromJSONUTF8Bytes: data, extensions: extensions, options: options)
-    } else {
-      throw JSONDecodingError.truncated
+
+    /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes``
+    /// containing a serialized array of messages in JSON format, interpreting the data as
+    /// UTF-8 encoded text.
+    ///
+    /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public static func array<Bytes: SwiftProtobufContiguousBytes>(
+        fromJSONUTF8Bytes jsonUTF8Bytes: Bytes,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws -> [Self] {
+        try self.array(
+            fromJSONUTF8Bytes: jsonUTF8Bytes,
+            extensions: SimpleExtensionMap(),
+            options: options
+        )
     }
-  }
 
-  /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes``
-  /// containing a serialized array of messages in JSON format, interpreting the data as
-  /// UTF-8 encoded text.
-  ///
-  /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public static func array<Bytes: SwiftProtobufContiguousBytes>(
-    fromJSONUTF8Bytes jsonUTF8Bytes: Bytes,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws -> [Self] {
-    return try self.array(fromJSONUTF8Bytes: jsonUTF8Bytes,
-                          extensions: SimpleExtensionMap(),
-                          options: options)
-  }
+    /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes``
+    /// containing a serialized array of messages in JSON format, interpreting the data as
+    /// UTF-8 encoded text.
+    ///
+    /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter extensions: The extension map to use with this decode
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public static func array<Bytes: SwiftProtobufContiguousBytes>(
+        fromJSONUTF8Bytes jsonUTF8Bytes: Bytes,
+        extensions: any ExtensionMap = SimpleExtensionMap(),
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws -> [Self] {
+        try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+            var array = [Self]()
 
-  /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes``
-  /// containing a serialized array of messages in JSON format, interpreting the data as
-  /// UTF-8 encoded text.
-  ///
-  /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter extensions: The extension map to use with this decode
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public static func array<Bytes: SwiftProtobufContiguousBytes>(
-    fromJSONUTF8Bytes jsonUTF8Bytes: Bytes,
-    extensions: any ExtensionMap = SimpleExtensionMap(),
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws -> [Self] {
-    return try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-      var array = [Self]()
+            if body.count > 0 {
+                var decoder = JSONDecoder(
+                    source: body,
+                    options: options,
+                    messageType: Self.self,
+                    extensions: extensions
+                )
+                try decoder.decodeRepeatedMessageField(value: &array)
+                if !decoder.scanner.complete {
+                    throw JSONDecodingError.trailingGarbage
+                }
+            }
 
-      if body.count > 0 {
-        var decoder = JSONDecoder(source: body, options: options,
-          messageType: Self.self, extensions: extensions)
-        try decoder.decodeRepeatedMessageField(value: &array)
-        if !decoder.scanner.complete {
-          throw JSONDecodingError.trailingGarbage
+            return array
         }
-      }
-
-      return array
     }
-  }
 
 }

+ 54 - 50
Sources/SwiftProtobuf/Message+JSONArrayAdditions_Data.swift

@@ -16,56 +16,60 @@ import Foundation
 
 /// JSON encoding and decoding methods for arrays of messages.
 extension Message {
-  /// Creates a new array of messages by decoding the given `Data`
-  /// containing a serialized array of messages in JSON format, interpreting the data as
-  /// UTF-8 encoded text.
-  ///
-  /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public static func array(
-    fromJSONUTF8Data jsonUTF8Data: Data,
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws -> [Self] {
-    return try self.array(fromJSONUTF8Bytes: jsonUTF8Data,
-                          extensions: SimpleExtensionMap(),
-                          options: options)
-  }
+    /// Creates a new array of messages by decoding the given `Data`
+    /// containing a serialized array of messages in JSON format, interpreting the data as
+    /// UTF-8 encoded text.
+    ///
+    /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public static func array(
+        fromJSONUTF8Data jsonUTF8Data: Data,
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws -> [Self] {
+        try self.array(
+            fromJSONUTF8Bytes: jsonUTF8Data,
+            extensions: SimpleExtensionMap(),
+            options: options
+        )
+    }
 
-  /// Creates a new array of messages by decoding the given `Data`
-  /// containing a serialized array of messages in JSON format, interpreting the data as
-  /// UTF-8 encoded text.
-  ///
-  /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
-  ///   as UTF-8 encoded text.
-  /// - Parameter extensions: The extension map to use with this decode
-  /// - Parameter options: The JSONDecodingOptions to use.
-  /// - Throws: ``JSONDecodingError`` if decoding fails.
-  public static func array(
-    fromJSONUTF8Data jsonUTF8Data: Data,
-    extensions: any ExtensionMap = SimpleExtensionMap(),
-    options: JSONDecodingOptions = JSONDecodingOptions()
-  ) throws -> [Self] {
-    return try array(fromJSONUTF8Bytes: jsonUTF8Data,
-                     extensions: extensions,
-                     options: options)
-  }
+    /// Creates a new array of messages by decoding the given `Data`
+    /// containing a serialized array of messages in JSON format, interpreting the data as
+    /// UTF-8 encoded text.
+    ///
+    /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented
+    ///   as UTF-8 encoded text.
+    /// - Parameter extensions: The extension map to use with this decode
+    /// - Parameter options: The JSONDecodingOptions to use.
+    /// - Throws: ``JSONDecodingError`` if decoding fails.
+    public static func array(
+        fromJSONUTF8Data jsonUTF8Data: Data,
+        extensions: any ExtensionMap = SimpleExtensionMap(),
+        options: JSONDecodingOptions = JSONDecodingOptions()
+    ) throws -> [Self] {
+        try array(
+            fromJSONUTF8Bytes: jsonUTF8Data,
+            extensions: extensions,
+            options: options
+        )
+    }
 
-  /// Returns a Data containing the UTF-8 JSON serialization of the messages.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to JSON.
-  ///
-  /// - Returns: A Data containing the JSON serialization of the messages.
-  /// - Parameters:
-  ///   - collection: The list of messages to encode.
-  ///   - options: The JSONEncodingOptions to use.
-  /// - Throws: ``JSONEncodingError`` if encoding fails.
-  public static func jsonUTF8Data<C: Collection>(
-    from collection: C,
-    options: JSONEncodingOptions = JSONEncodingOptions()
-  ) throws -> Data where C.Iterator.Element == Self {
-    try jsonUTF8Bytes(from: collection, options: options)
-  }
+    /// Returns a Data containing the UTF-8 JSON serialization of the messages.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to JSON.
+    ///
+    /// - Returns: A Data containing the JSON serialization of the messages.
+    /// - Parameters:
+    ///   - collection: The list of messages to encode.
+    ///   - options: The JSONEncodingOptions to use.
+    /// - Throws: ``JSONEncodingError`` if encoding fails.
+    public static func jsonUTF8Data<C: Collection>(
+        from collection: C,
+        options: JSONEncodingOptions = JSONEncodingOptions()
+    ) throws -> Data where C.Iterator.Element == Self {
+        try jsonUTF8Bytes(from: collection, options: options)
+    }
 }

+ 89 - 85
Sources/SwiftProtobuf/Message+TextFormatAdditions.swift

@@ -16,95 +16,99 @@ import Foundation
 
 /// Text format encoding and decoding methods for messages.
 extension Message {
-  /// Returns a string containing the Protocol Buffer text format serialization
-  /// of the message.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to text format.
-  ///
-  /// - Returns: A string containing the text format serialization of the
-  ///   message.
-  public func textFormatString() -> String {
-    // This is implemented as a separate zero-argument function
-    // to preserve binary compatibility.
-    return textFormatString(options: TextFormatEncodingOptions())
-  }
+    /// Returns a string containing the Protocol Buffer text format serialization
+    /// of the message.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to text format.
+    ///
+    /// - Returns: A string containing the text format serialization of the
+    ///   message.
+    public func textFormatString() -> String {
+        // This is implemented as a separate zero-argument function
+        // to preserve binary compatibility.
+        textFormatString(options: TextFormatEncodingOptions())
+    }
+
+    /// Returns a string containing the Protocol Buffer text format serialization
+    /// of the message.
+    ///
+    /// Unlike binary encoding, presence of required fields is not enforced when
+    /// serializing to text format.
+    ///
+    /// - Returns: A string containing the text format serialization of the message.
+    /// - Parameters:
+    ///   - options: The TextFormatEncodingOptions to use.
+    public func textFormatString(
+        options: TextFormatEncodingOptions
+    ) -> String {
+        var visitor = TextFormatEncodingVisitor(message: self, options: options)
+        if let any = self as? Google_Protobuf_Any {
+            any._storage.textTraverse(visitor: &visitor)
+        } else {
+            // Although the general traversal/encoding infrastructure supports
+            // throwing errors (needed for JSON/Binary WKTs support, binary format
+            // missing required fields); TextEncoding never actually does throw.
+            try! traverse(visitor: &visitor)
+        }
+        return visitor.result
+    }
 
-  /// Returns a string containing the Protocol Buffer text format serialization
-  /// of the message.
-  ///
-  /// Unlike binary encoding, presence of required fields is not enforced when
-  /// serializing to text format.
-  ///
-  /// - Returns: A string containing the text format serialization of the message.
-  /// - Parameters:
-  ///   - options: The TextFormatEncodingOptions to use.
-  public func textFormatString(
-    options: TextFormatEncodingOptions
-  ) -> String {
-    var visitor = TextFormatEncodingVisitor(message: self, options: options)
-    if let any = self as? Google_Protobuf_Any {
-      any._storage.textTraverse(visitor: &visitor)
-    } else {
-      // Although the general traversal/encoding infrastructure supports
-      // throwing errors (needed for JSON/Binary WKTs support, binary format
-      // missing required fields); TextEncoding never actually does throw.
-      try! traverse(visitor: &visitor)
+    /// Creates a new message by decoding the given string containing a
+    /// serialized message in Protocol Buffer text format.
+    ///
+    /// - Parameters:
+    ///   - textFormatString: The text format string to decode.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    /// - Throws: ``SwiftProtobufError`` on failure.
+    // TODO: delete this (and keep the one with the extra param instead) when we break API
+    public init(
+        textFormatString: String,
+        extensions: (any ExtensionMap)? = nil
+    ) throws {
+        try self.init(
+            textFormatString: textFormatString,
+            options: TextFormatDecodingOptions(),
+            extensions: extensions
+        )
     }
-    return visitor.result
-  }
-    
-  /// Creates a new message by decoding the given string containing a
-  /// serialized message in Protocol Buffer text format.
-  ///
-  /// - Parameters:
-  ///   - textFormatString: The text format string to decode.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  /// - Throws: ``SwiftProtobufError`` on failure.
-  // TODO: delete this (and keep the one with the extra param instead) when we break API
-  public init(
-    textFormatString: String,
-    extensions: (any ExtensionMap)? = nil
-  ) throws {
-    try self.init(textFormatString: textFormatString,
-                  options: TextFormatDecodingOptions(),
-                  extensions: extensions)
-  }
 
-  /// Creates a new message by decoding the given string containing a
-  /// serialized message in Protocol Buffer text format.
-  ///
-  /// - Parameters:
-  ///   - textFormatString: The text format string to decode.
-  ///   - options: The ``TextFormatDecodingOptions`` to use.
-  ///   - extensions: An ``ExtensionMap`` used to look up and decode any
-  ///     extensions in this message or messages nested within this message's
-  ///     fields.
-  /// - Throws: ``TextFormatDecodingError`` on failure.
-  public init(
-    textFormatString: String,
-    options: TextFormatDecodingOptions = TextFormatDecodingOptions(),
-    extensions: (any ExtensionMap)? = nil
-  ) throws {
-    self.init()
-    if !textFormatString.isEmpty {
-      if let data = textFormatString.data(using: String.Encoding.utf8) {
-        try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-          if let baseAddress = body.baseAddress, body.count > 0 {
-            var decoder = try TextFormatDecoder(messageType: Self.self,
-                                                utf8Pointer: baseAddress,
-                                                count: body.count,
-                                                options: options,
-                                                extensions: extensions)
-            try decodeMessage(decoder: &decoder)
-            if !decoder.complete {
-              throw TextFormatDecodingError.trailingGarbage
+    /// Creates a new message by decoding the given string containing a
+    /// serialized message in Protocol Buffer text format.
+    ///
+    /// - Parameters:
+    ///   - textFormatString: The text format string to decode.
+    ///   - options: The ``TextFormatDecodingOptions`` to use.
+    ///   - extensions: An ``ExtensionMap`` used to look up and decode any
+    ///     extensions in this message or messages nested within this message's
+    ///     fields.
+    /// - Throws: ``TextFormatDecodingError`` on failure.
+    public init(
+        textFormatString: String,
+        options: TextFormatDecodingOptions = TextFormatDecodingOptions(),
+        extensions: (any ExtensionMap)? = nil
+    ) throws {
+        self.init()
+        if !textFormatString.isEmpty {
+            if let data = textFormatString.data(using: String.Encoding.utf8) {
+                try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
+                    if let baseAddress = body.baseAddress, body.count > 0 {
+                        var decoder = try TextFormatDecoder(
+                            messageType: Self.self,
+                            utf8Pointer: baseAddress,
+                            count: body.count,
+                            options: options,
+                            extensions: extensions
+                        )
+                        try decodeMessage(decoder: &decoder)
+                        if !decoder.complete {
+                            throw TextFormatDecodingError.trailingGarbage
+                        }
+                    }
+                }
             }
-          }
         }
-      }
     }
-  }
 }

+ 152 - 152
Sources/SwiftProtobuf/Message.swift

@@ -33,141 +33,141 @@
 /// default implementations of the below methods and properties.
 @preconcurrency
 public protocol Message: Sendable, CustomDebugStringConvertible {
-  /// Creates a new message with all of its fields initialized to their default
-  /// values.
-  init()
-
-  // Metadata
-  // Basic facts about this class and the proto message it was generated from
-  // Used by various encoders and decoders
-
-  /// The fully-scoped name of the message from the original .proto file,
-  /// including any relevant package name.
-  static var protoMessageName: String { get }
-
-  /// True if all required fields (if any) on this message and any nested
-  /// messages (recursively) have values set; otherwise, false.
-  var isInitialized: Bool { get }
-
-  /// Some formats include enough information to transport fields that were
-  /// not known at generation time. When encountered, they are stored here.
-  var unknownFields: UnknownStorage { get set }
-
-  //
-  // General serialization/deserialization machinery
-  //
-
-  /// Decode all of the fields from the given decoder.
-  ///
-  /// This is a simple loop that repeatedly gets the next field number
-  /// from `decoder.nextFieldNumber()` and then uses the number returned
-  /// and the type information from the original .proto file to decide
-  /// what type of data should be decoded for that field.  The corresponding
-  /// method on the decoder is then called to get the field value.
-  ///
-  /// This is the core method used by the deserialization machinery. It is
-  /// `public` to enable users to implement their own encoding formats by
-  /// conforming to `Decoder`; it should not be called otherwise.
-  ///
-  /// Note that this is not specific to binary encodng; formats that use
-  /// textual identifiers translate those to field numbers and also go
-  /// through this to decode messages.
-  ///
-  /// - Parameters:
-  ///   - decoder: a `Decoder`; the `Message` will call the method
-  ///     corresponding to the type of this field.
-  /// - Throws: an error on failure or type mismatch.  The type of error
-  ///     thrown depends on which decoder is used.
-  mutating func decodeMessage<D: Decoder>(decoder: inout D) throws
-
-  /// Traverses the fields of the message, calling the appropriate methods
-  /// of the passed `Visitor` object.
-  ///
-  /// This is used internally by:
-  ///
-  /// * Protobuf binary serialization
-  /// * JSON serialization (with some twists to account for specialty JSON)
-  /// * Protobuf Text serialization
-  /// * `Hashable` computation
-  ///
-  /// Conceptually, serializers create visitor objects that are
-  /// then passed recursively to every message and field via generated
-  /// `traverse` methods.  The details get a little involved due to
-  /// the need to allow particular messages to override particular
-  /// behaviors for specific encodings, but the general idea is quite simple.
-  func traverse<V: Visitor>(visitor: inout V) throws
-
-  // Standard utility properties and methods.
-  // Most of these are simple wrappers on top of the visitor machinery.
-  // They are implemented in the protocol, not in the generated structs,
-  // so can be overridden in user code by defining custom extensions to
-  // the generated struct.
-
-  /// An implementation of hash(into:) to provide conformance with the
-  /// `Hashable` protocol.
-  func hash(into hasher: inout Hasher)
-
-  /// Helper to compare `Message`s when not having a specific type to use
-  /// normal `Equatable`. `Equatable` is provided with specific generated
-  /// types.
-  func isEqualTo(message: any Message) -> Bool
+    /// Creates a new message with all of its fields initialized to their default
+    /// values.
+    init()
+
+    // Metadata
+    // Basic facts about this class and the proto message it was generated from
+    // Used by various encoders and decoders
+
+    /// The fully-scoped name of the message from the original .proto file,
+    /// including any relevant package name.
+    static var protoMessageName: String { get }
+
+    /// True if all required fields (if any) on this message and any nested
+    /// messages (recursively) have values set; otherwise, false.
+    var isInitialized: Bool { get }
+
+    /// Some formats include enough information to transport fields that were
+    /// not known at generation time. When encountered, they are stored here.
+    var unknownFields: UnknownStorage { get set }
+
+    //
+    // General serialization/deserialization machinery
+    //
+
+    /// Decode all of the fields from the given decoder.
+    ///
+    /// This is a simple loop that repeatedly gets the next field number
+    /// from `decoder.nextFieldNumber()` and then uses the number returned
+    /// and the type information from the original .proto file to decide
+    /// what type of data should be decoded for that field.  The corresponding
+    /// method on the decoder is then called to get the field value.
+    ///
+    /// This is the core method used by the deserialization machinery. It is
+    /// `public` to enable users to implement their own encoding formats by
+    /// conforming to `Decoder`; it should not be called otherwise.
+    ///
+    /// Note that this is not specific to binary encodng; formats that use
+    /// textual identifiers translate those to field numbers and also go
+    /// through this to decode messages.
+    ///
+    /// - Parameters:
+    ///   - decoder: a `Decoder`; the `Message` will call the method
+    ///     corresponding to the type of this field.
+    /// - Throws: an error on failure or type mismatch.  The type of error
+    ///     thrown depends on which decoder is used.
+    mutating func decodeMessage<D: Decoder>(decoder: inout D) throws
+
+    /// Traverses the fields of the message, calling the appropriate methods
+    /// of the passed `Visitor` object.
+    ///
+    /// This is used internally by:
+    ///
+    /// * Protobuf binary serialization
+    /// * JSON serialization (with some twists to account for specialty JSON)
+    /// * Protobuf Text serialization
+    /// * `Hashable` computation
+    ///
+    /// Conceptually, serializers create visitor objects that are
+    /// then passed recursively to every message and field via generated
+    /// `traverse` methods.  The details get a little involved due to
+    /// the need to allow particular messages to override particular
+    /// behaviors for specific encodings, but the general idea is quite simple.
+    func traverse<V: Visitor>(visitor: inout V) throws
+
+    // Standard utility properties and methods.
+    // Most of these are simple wrappers on top of the visitor machinery.
+    // They are implemented in the protocol, not in the generated structs,
+    // so can be overridden in user code by defining custom extensions to
+    // the generated struct.
+
+    /// An implementation of hash(into:) to provide conformance with the
+    /// `Hashable` protocol.
+    func hash(into hasher: inout Hasher)
+
+    /// Helper to compare `Message`s when not having a specific type to use
+    /// normal `Equatable`. `Equatable` is provided with specific generated
+    /// types.
+    func isEqualTo(message: any Message) -> Bool
 }
 
 extension Message {
-  /// Generated proto2 messages that contain required fields, nested messages
-  /// that contain required fields, and/or extensions will provide their own
-  /// implementation of this property that tests that all required fields are
-  /// set. Users of the generated code SHOULD NOT override this property.
-  public var isInitialized: Bool {
-    // The generated code will include a specialization as needed.
-    return true
-  }
-
-  /// A hash based on the message's full contents.
-  public func hash(into hasher: inout Hasher) {
-    var visitor = HashVisitor(hasher)
-    try? traverse(visitor: &visitor)
-    hasher = visitor.hasher
-  }
-
-  /// A description generated by recursively visiting all fields in the message,
-  /// including messages.
-  public var debugDescription: String {
-    #if DEBUG
-    // TODO Ideally there would be something like serializeText() that can
-    // take a prefix so we could do something like:
-    //   [class name](
-    //      [text format]
-    //   )
-    let className = String(reflecting: type(of: self))
-    let header = "\(className):\n"
-    return header + textFormatString()
-    #else
-    return String(reflecting: type(of: self))
-    #endif
-  }
-
-  /// Creates an instance of the message type on which this method is called,
-  /// executes the given block passing the message in as its sole `inout`
-  /// argument, and then returns the message.
-  ///
-  /// This method acts essentially as a "builder" in that the initialization of
-  /// the message is captured within the block, allowing the returned value to
-  /// be set in an immutable variable. For example,
-  ///
-  ///     let msg = MyMessage.with { $0.myField = "foo" }
-  ///     msg.myOtherField = 5  // error: msg is immutable
-  ///
-  /// - Parameter populator: A block or function that populates the new message,
-  ///   which is passed into the block as an `inout` argument.
-  /// - Returns: The message after execution of the block.
-  public static func with(
-    _ populator: (inout Self) throws -> ()
-  ) rethrows -> Self {
-    var message = Self()
-    try populator(&message)
-    return message
-  }
+    /// Generated proto2 messages that contain required fields, nested messages
+    /// that contain required fields, and/or extensions will provide their own
+    /// implementation of this property that tests that all required fields are
+    /// set. Users of the generated code SHOULD NOT override this property.
+    public var isInitialized: Bool {
+        // The generated code will include a specialization as needed.
+        true
+    }
+
+    /// A hash based on the message's full contents.
+    public func hash(into hasher: inout Hasher) {
+        var visitor = HashVisitor(hasher)
+        try? traverse(visitor: &visitor)
+        hasher = visitor.hasher
+    }
+
+    /// A description generated by recursively visiting all fields in the message,
+    /// including messages.
+    public var debugDescription: String {
+        #if DEBUG
+        // TODO Ideally there would be something like serializeText() that can
+        // take a prefix so we could do something like:
+        //   [class name](
+        //      [text format]
+        //   )
+        let className = String(reflecting: type(of: self))
+        let header = "\(className):\n"
+        return header + textFormatString()
+        #else
+        return String(reflecting: type(of: self))
+        #endif
+    }
+
+    /// Creates an instance of the message type on which this method is called,
+    /// executes the given block passing the message in as its sole `inout`
+    /// argument, and then returns the message.
+    ///
+    /// This method acts essentially as a "builder" in that the initialization of
+    /// the message is captured within the block, allowing the returned value to
+    /// be set in an immutable variable. For example,
+    ///
+    ///     let msg = MyMessage.with { $0.myField = "foo" }
+    ///     msg.myOtherField = 5  // error: msg is immutable
+    ///
+    /// - Parameter populator: A block or function that populates the new message,
+    ///   which is passed into the block as an `inout` argument.
+    /// - Returns: The message after execution of the block.
+    public static func with(
+        _ populator: (inout Self) throws -> Void
+    ) rethrows -> Self {
+        var message = Self()
+        try populator(&message)
+        return message
+    }
 }
 
 /// Implementation base for all messages; not intended for client use.
@@ -181,28 +181,28 @@ extension Message {
 @preconcurrency
 public protocol _MessageImplementationBase: Message, Hashable {
 
-  // Legacy function; no longer used, but left to maintain source compatibility.
-  func _protobuf_generated_isEqualTo(other: Self) -> Bool
+    // Legacy function; no longer used, but left to maintain source compatibility.
+    func _protobuf_generated_isEqualTo(other: Self) -> Bool
 }
 
 extension _MessageImplementationBase {
-  public func isEqualTo(message: any Message) -> Bool {
-    guard let other = message as? Self else {
-      return false
+    public func isEqualTo(message: any Message) -> Bool {
+        guard let other = message as? Self else {
+            return false
+        }
+        return self == other
+    }
+
+    // Legacy default implementation that is used by old generated code, current
+    // versions of the plugin/generator provide this directly, but this is here
+    // just to avoid breaking source compatibility.
+    public static func == (lhs: Self, rhs: Self) -> Bool {
+        lhs._protobuf_generated_isEqualTo(other: rhs)
+    }
+
+    // Legacy function that is generated by old versions of the plugin/generator,
+    // defaulted to keep things simple without changing the api surface.
+    public func _protobuf_generated_isEqualTo(other: Self) -> Bool {
+        self == other
     }
-    return self == other
-  }
-
-  // Legacy default implementation that is used by old generated code, current
-  // versions of the plugin/generator provide this directly, but this is here
-  // just to avoid breaking source compatibility.
-  public static func ==(lhs: Self, rhs: Self) -> Bool {
-    return lhs._protobuf_generated_isEqualTo(other: rhs)
-  }
-
-  // Legacy function that is generated by old versions of the plugin/generator,
-  // defaulted to keep things simple without changing the api surface.
-  public func _protobuf_generated_isEqualTo(other: Self) -> Bool {
-    return self == other
-  }
 }

+ 1 - 1
Sources/SwiftProtobuf/MessageExtension.swift

@@ -37,6 +37,6 @@ public final class MessageExtension<FieldType: ExtensionField, MessageType: Mess
         self.messageType = MessageType.self
     }
     public func _protobuf_newField<D: Decoder>(decoder: inout D) throws -> (any AnyExtensionField)? {
-        return try FieldType(protobufExtension: self, decoder: &decoder)
+        try FieldType(protobufExtension: self, decoder: &decoder)
     }
 }

+ 226 - 220
Sources/SwiftProtobuf/NameMap.swift

@@ -39,32 +39,36 @@ private func toJsonFieldName(_ s: String) -> String {
 /// Allocate static memory buffers to intern UTF-8
 /// string data.  Track the buffers and release all of those buffers
 /// in case we ever get deallocated.
-fileprivate class InternPool {
-  private var interned = [UnsafeRawBufferPointer]()
-
-  func intern(utf8: String.UTF8View) -> UnsafeRawBufferPointer {
-    let mutable = UnsafeMutableRawBufferPointer.allocate(byteCount: utf8.count,
-                                                         alignment: MemoryLayout<UInt8>.alignment)
-    mutable.copyBytes(from: utf8)
-    let immutable = UnsafeRawBufferPointer(mutable)
-    interned.append(immutable)
-    return immutable
-  }
-
-  func intern(utf8Ptr: UnsafeBufferPointer<UInt8>) -> UnsafeRawBufferPointer {
-    let mutable = UnsafeMutableRawBufferPointer.allocate(byteCount: utf8Ptr.count,
-                                                         alignment: MemoryLayout<UInt8>.alignment)
-    mutable.copyBytes(from: utf8Ptr)
-    let immutable = UnsafeRawBufferPointer(mutable)
-    interned.append(immutable)
-    return immutable
-  }
-
-  deinit {
-    for buff in interned {
-        buff.deallocate()
+private class InternPool {
+    private var interned = [UnsafeRawBufferPointer]()
+
+    func intern(utf8: String.UTF8View) -> UnsafeRawBufferPointer {
+        let mutable = UnsafeMutableRawBufferPointer.allocate(
+            byteCount: utf8.count,
+            alignment: MemoryLayout<UInt8>.alignment
+        )
+        mutable.copyBytes(from: utf8)
+        let immutable = UnsafeRawBufferPointer(mutable)
+        interned.append(immutable)
+        return immutable
+    }
+
+    func intern(utf8Ptr: UnsafeBufferPointer<UInt8>) -> UnsafeRawBufferPointer {
+        let mutable = UnsafeMutableRawBufferPointer.allocate(
+            byteCount: utf8Ptr.count,
+            alignment: MemoryLayout<UInt8>.alignment
+        )
+        mutable.copyBytes(from: utf8Ptr)
+        let immutable = UnsafeRawBufferPointer(mutable)
+        interned.append(immutable)
+        return immutable
+    }
+
+    deinit {
+        for buff in interned {
+            buff.deallocate()
+        }
     }
-  }
 }
 
 /// An immutable bidirectional mapping between field/enum-case names
@@ -74,215 +78,217 @@ fileprivate class InternPool {
 /// users who do not use text-based serialization formats.
 public struct _NameMap: ExpressibleByDictionaryLiteral {
 
-  /// An immutable interned string container.  The `utf8Start` pointer
-  /// is guaranteed valid for the lifetime of the `NameMap` that you
-  /// fetched it from.  Since `NameMap`s are only instantiated as
-  /// immutable static values, that should be the lifetime of the
-  /// program.
-  ///
-  /// Internally, this uses `StaticString` (which refers to a fixed
-  /// block of UTF-8 data) where possible.  In cases where the string
-  /// has to be computed, it caches the UTF-8 bytes in an
-  /// unmovable and immutable heap area.
-  internal struct Name: Hashable, CustomStringConvertible {
-    // This should not be used outside of this file, as it requires
-    // coordinating the lifecycle with the lifecycle of the pool
-    // where the raw UTF8 gets interned.
-    fileprivate init(staticString: StaticString, pool: InternPool) {
-        self.nameString = .staticString(staticString)
-        if staticString.hasPointerRepresentation {
-            self.utf8Buffer = UnsafeRawBufferPointer(start: staticString.utf8Start,
-                                                     count: staticString.utf8CodeUnitCount)
-        } else {
-            self.utf8Buffer = staticString.withUTF8Buffer { pool.intern(utf8Ptr: $0) }
+    /// An immutable interned string container.  The `utf8Start` pointer
+    /// is guaranteed valid for the lifetime of the `NameMap` that you
+    /// fetched it from.  Since `NameMap`s are only instantiated as
+    /// immutable static values, that should be the lifetime of the
+    /// program.
+    ///
+    /// Internally, this uses `StaticString` (which refers to a fixed
+    /// block of UTF-8 data) where possible.  In cases where the string
+    /// has to be computed, it caches the UTF-8 bytes in an
+    /// unmovable and immutable heap area.
+    internal struct Name: Hashable, CustomStringConvertible {
+        // This should not be used outside of this file, as it requires
+        // coordinating the lifecycle with the lifecycle of the pool
+        // where the raw UTF8 gets interned.
+        fileprivate init(staticString: StaticString, pool: InternPool) {
+            self.nameString = .staticString(staticString)
+            if staticString.hasPointerRepresentation {
+                self.utf8Buffer = UnsafeRawBufferPointer(
+                    start: staticString.utf8Start,
+                    count: staticString.utf8CodeUnitCount
+                )
+            } else {
+                self.utf8Buffer = staticString.withUTF8Buffer { pool.intern(utf8Ptr: $0) }
+            }
         }
-    }
 
-    // This should not be used outside of this file, as it requires
-    // coordinating the lifecycle with the lifecycle of the pool
-    // where the raw UTF8 gets interned.
-    fileprivate init(string: String, pool: InternPool) {
-      let utf8 = string.utf8
-      self.utf8Buffer = pool.intern(utf8: utf8)
-      self.nameString = .string(string)
+        // This should not be used outside of this file, as it requires
+        // coordinating the lifecycle with the lifecycle of the pool
+        // where the raw UTF8 gets interned.
+        fileprivate init(string: String, pool: InternPool) {
+            let utf8 = string.utf8
+            self.utf8Buffer = pool.intern(utf8: utf8)
+            self.nameString = .string(string)
+        }
+
+        // This is for building a transient `Name` object sufficient for lookup purposes.
+        // It MUST NOT be exposed outside of this file.
+        fileprivate init(transientUtf8Buffer: UnsafeRawBufferPointer) {
+            self.nameString = .staticString("")
+            self.utf8Buffer = transientUtf8Buffer
+        }
+
+        private(set) var utf8Buffer: UnsafeRawBufferPointer
+
+        private enum NameString {
+            case string(String)
+            case staticString(StaticString)
+        }
+        private var nameString: NameString
+
+        public var description: String {
+            switch nameString {
+            case .string(let s): return s
+            case .staticString(let s): return s.description
+            }
+        }
+
+        public func hash(into hasher: inout Hasher) {
+            for byte in utf8Buffer {
+                hasher.combine(byte)
+            }
+        }
+
+        public static func == (lhs: Name, rhs: Name) -> Bool {
+            if lhs.utf8Buffer.count != rhs.utf8Buffer.count {
+                return false
+            }
+            return lhs.utf8Buffer.elementsEqual(rhs.utf8Buffer)
+        }
     }
 
-    // This is for building a transient `Name` object sufficient for lookup purposes.
-    // It MUST NOT be exposed outside of this file.
-    fileprivate init(transientUtf8Buffer: UnsafeRawBufferPointer) {
-        self.nameString = .staticString("")
-        self.utf8Buffer = transientUtf8Buffer
+    /// The JSON and proto names for a particular field, enum case, or extension.
+    internal struct Names {
+        private(set) var json: Name?
+        private(set) var proto: Name
     }
 
-    private(set) var utf8Buffer: UnsafeRawBufferPointer
+    /// A description of the names for a particular field or enum case.
+    /// The different forms here let us minimize the amount of string
+    /// data that we store in the binary.
+    ///
+    /// These are only used in the generated code to initialize a NameMap.
+    public enum NameDescription {
+
+        /// The proto (text format) name and the JSON name are the same string.
+        case same(proto: StaticString)
+
+        /// The JSON name can be computed from the proto string
+        case standard(proto: StaticString)
 
-    private enum NameString {
-      case string(String)
-      case staticString(StaticString)
+        /// The JSON and text format names are just different.
+        case unique(proto: StaticString, json: StaticString)
+
+        /// Used for enum cases only to represent a value's primary proto name (the
+        /// first defined case) and its aliases. The JSON and text format names for
+        /// enums are always the same.
+        case aliased(proto: StaticString, aliases: [StaticString])
     }
-    private var nameString: NameString
 
-    public var description: String {
-      switch nameString {
-      case .string(let s): return s
-      case .staticString(let s): return s.description
-      }
+    private var internPool = InternPool()
+
+    /// The mapping from field/enum-case numbers to names.
+    private var numberToNameMap: [Int: Names] = [:]
+
+    /// The mapping from proto/text names to field/enum-case numbers.
+    private var protoToNumberMap: [Name: Int] = [:]
+
+    /// The mapping from JSON names to field/enum-case numbers.
+    /// Note that this also contains all of the proto/text names,
+    /// as required by Google's spec for protobuf JSON.
+    private var jsonToNumberMap: [Name: Int] = [:]
+
+    /// Creates a new empty field/enum-case name/number mapping.
+    public init() {}
+
+    /// Build the bidirectional maps between numbers and proto/JSON names.
+    public init(dictionaryLiteral elements: (Int, NameDescription)...) {
+        for (number, description) in elements {
+            switch description {
+
+            case .same(proto: let p):
+                let protoName = Name(staticString: p, pool: internPool)
+                let names = Names(json: protoName, proto: protoName)
+                numberToNameMap[number] = names
+                protoToNumberMap[protoName] = number
+                jsonToNumberMap[protoName] = number
+
+            case .standard(proto: let p):
+                let protoName = Name(staticString: p, pool: internPool)
+                let jsonString = toJsonFieldName(protoName.description)
+                let jsonName = Name(string: jsonString, pool: internPool)
+                let names = Names(json: jsonName, proto: protoName)
+                numberToNameMap[number] = names
+                protoToNumberMap[protoName] = number
+                jsonToNumberMap[protoName] = number
+                jsonToNumberMap[jsonName] = number
+
+            case .unique(proto: let p, json: let j):
+                let jsonName = Name(staticString: j, pool: internPool)
+                let protoName = Name(staticString: p, pool: internPool)
+                let names = Names(json: jsonName, proto: protoName)
+                numberToNameMap[number] = names
+                protoToNumberMap[protoName] = number
+                jsonToNumberMap[protoName] = number
+                jsonToNumberMap[jsonName] = number
+
+            case .aliased(proto: let p, let aliases):
+                let protoName = Name(staticString: p, pool: internPool)
+                let names = Names(json: protoName, proto: protoName)
+                numberToNameMap[number] = names
+                protoToNumberMap[protoName] = number
+                jsonToNumberMap[protoName] = number
+                for alias in aliases {
+                    let protoName = Name(staticString: alias, pool: internPool)
+                    protoToNumberMap[protoName] = number
+                    jsonToNumberMap[protoName] = number
+                }
+            }
+        }
     }
 
-    public func hash(into hasher: inout Hasher) {
-      for byte in utf8Buffer {
-        hasher.combine(byte)
-      }
+    /// Returns the name bundle for the field/enum-case with the given number, or
+    /// `nil` if there is no match.
+    internal func names(for number: Int) -> Names? {
+        numberToNameMap[number]
     }
 
-    public static func ==(lhs: Name, rhs: Name) -> Bool {
-      if lhs.utf8Buffer.count != rhs.utf8Buffer.count {
-        return false
-      }
-      return lhs.utf8Buffer.elementsEqual(rhs.utf8Buffer)
+    /// Returns the field/enum-case number that has the given JSON name,
+    /// or `nil` if there is no match.
+    ///
+    /// This is used by the Text format parser to look up field or enum
+    /// names using a direct reference to the un-decoded UTF8 bytes.
+    internal func number(forProtoName raw: UnsafeRawBufferPointer) -> Int? {
+        let n = Name(transientUtf8Buffer: raw)
+        return protoToNumberMap[n]
     }
-  }
-
-  /// The JSON and proto names for a particular field, enum case, or extension.
-  internal struct Names {
-    private(set) var json: Name?
-    private(set) var proto: Name
-  }
-
-  /// A description of the names for a particular field or enum case.
-  /// The different forms here let us minimize the amount of string
-  /// data that we store in the binary.
-  ///
-  /// These are only used in the generated code to initialize a NameMap.
-  public enum NameDescription {
-
-    /// The proto (text format) name and the JSON name are the same string.
-    case same(proto: StaticString)
-
-    /// The JSON name can be computed from the proto string
-    case standard(proto: StaticString)
-
-    /// The JSON and text format names are just different.
-    case unique(proto: StaticString, json: StaticString)
-
-    /// Used for enum cases only to represent a value's primary proto name (the
-    /// first defined case) and its aliases. The JSON and text format names for
-    /// enums are always the same.
-    case aliased(proto: StaticString, aliases: [StaticString])
-  }
-
-  private var internPool = InternPool()
-
-  /// The mapping from field/enum-case numbers to names.
-  private var numberToNameMap: [Int: Names] = [:]
-
-  /// The mapping from proto/text names to field/enum-case numbers.
-  private var protoToNumberMap: [Name: Int] = [:]
-
-  /// The mapping from JSON names to field/enum-case numbers.
-  /// Note that this also contains all of the proto/text names,
-  /// as required by Google's spec for protobuf JSON.
-  private var jsonToNumberMap: [Name: Int] = [:]
-
-  /// Creates a new empty field/enum-case name/number mapping.
-  public init() {}
-
-  /// Build the bidirectional maps between numbers and proto/JSON names.
-  public init(dictionaryLiteral elements: (Int, NameDescription)...) {
-    for (number, description) in elements {
-      switch description {
-
-      case .same(proto: let p):
-        let protoName = Name(staticString: p, pool: internPool)
-        let names = Names(json: protoName, proto: protoName)
-        numberToNameMap[number] = names
-        protoToNumberMap[protoName] = number
-        jsonToNumberMap[protoName] = number
-
-      case .standard(proto: let p):
-        let protoName = Name(staticString: p, pool: internPool)
-        let jsonString = toJsonFieldName(protoName.description)
-        let jsonName = Name(string: jsonString, pool: internPool)
-        let names = Names(json: jsonName, proto: protoName)
-        numberToNameMap[number] = names
-        protoToNumberMap[protoName] = number
-        jsonToNumberMap[protoName] = number
-        jsonToNumberMap[jsonName] = number
-
-      case .unique(proto: let p, json: let j):
-        let jsonName = Name(staticString: j, pool: internPool)
-        let protoName = Name(staticString: p, pool: internPool)
-        let names = Names(json: jsonName, proto: protoName)
-        numberToNameMap[number] = names
-        protoToNumberMap[protoName] = number
-        jsonToNumberMap[protoName] = number
-        jsonToNumberMap[jsonName] = number
-
-      case .aliased(proto: let p, aliases: let aliases):
-        let protoName = Name(staticString: p, pool: internPool)
-        let names = Names(json: protoName, proto: protoName)
-        numberToNameMap[number] = names
-        protoToNumberMap[protoName] = number
-        jsonToNumberMap[protoName] = number
-        for alias in aliases {
-            let protoName = Name(staticString: alias, pool: internPool)
-            protoToNumberMap[protoName] = number
-            jsonToNumberMap[protoName] = number
+
+    /// Returns the field/enum-case number that has the given JSON name,
+    /// or `nil` if there is no match.
+    ///
+    /// This accepts a regular `String` and is used in JSON parsing
+    /// only when a field name or enum name was decoded from a string
+    /// containing backslash escapes.
+    ///
+    /// JSON parsing must interpret *both* the JSON name of the
+    /// field/enum-case provided by the descriptor *as well as* its
+    /// original proto/text name.
+    internal func number(forJSONName name: String) -> Int? {
+        let utf8 = Array(name.utf8)
+        return utf8.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
+            let n = Name(transientUtf8Buffer: buffer)
+            return jsonToNumberMap[n]
         }
-      }
     }
-  }
-
-  /// Returns the name bundle for the field/enum-case with the given number, or
-  /// `nil` if there is no match.
-  internal func names(for number: Int) -> Names? {
-    return numberToNameMap[number]
-  }
-
-  /// Returns the field/enum-case number that has the given JSON name,
-  /// or `nil` if there is no match.
-  ///
-  /// This is used by the Text format parser to look up field or enum
-  /// names using a direct reference to the un-decoded UTF8 bytes.
-  internal func number(forProtoName raw: UnsafeRawBufferPointer) -> Int? {
-    let n = Name(transientUtf8Buffer: raw)
-    return protoToNumberMap[n]
-  }
-
-  /// Returns the field/enum-case number that has the given JSON name,
-  /// or `nil` if there is no match.
-  ///
-  /// This accepts a regular `String` and is used in JSON parsing
-  /// only when a field name or enum name was decoded from a string
-  /// containing backslash escapes.
-  ///
-  /// JSON parsing must interpret *both* the JSON name of the
-  /// field/enum-case provided by the descriptor *as well as* its
-  /// original proto/text name.
-  internal func number(forJSONName name: String) -> Int? {
-    let utf8 = Array(name.utf8)
-    return utf8.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
-      let n = Name(transientUtf8Buffer: buffer)
-      return jsonToNumberMap[n]
+
+    /// Returns the field/enum-case number that has the given JSON name,
+    /// or `nil` if there is no match.
+    ///
+    /// This is used by the JSON parser when a field name or enum name
+    /// required no special processing.  As a result, we can avoid
+    /// copying the name and look up the number using a direct reference
+    /// to the un-decoded UTF8 bytes.
+    internal func number(forJSONName raw: UnsafeRawBufferPointer) -> Int? {
+        let n = Name(transientUtf8Buffer: raw)
+        return jsonToNumberMap[n]
+    }
+
+    /// Returns all proto names
+    internal var names: [Name] {
+        numberToNameMap.map(\.value.proto)
     }
-  }
-
-  /// Returns the field/enum-case number that has the given JSON name,
-  /// or `nil` if there is no match.
-  ///
-  /// This is used by the JSON parser when a field name or enum name
-  /// required no special processing.  As a result, we can avoid
-  /// copying the name and look up the number using a direct reference
-  /// to the un-decoded UTF8 bytes.
-  internal func number(forJSONName raw: UnsafeRawBufferPointer) -> Int? {
-    let n = Name(transientUtf8Buffer: raw)
-    return jsonToNumberMap[n]
-  }
-  
-  /// Returns all proto names
-  internal var names: [Name] {
-    numberToNameMap.map(\.value.proto)
-  }
 }
 
 // The `_NameMap` (and supporting types) are only mutated during their initial
@@ -294,6 +300,6 @@ public struct _NameMap: ExpressibleByDictionaryLiteral {
 // https://github.com/apple/swift-protobuf/issues/1561 is also opened to revisit
 // the `_NameMap` generally as it dates back to the days before Swift perferred
 // the UTF-8 internal encoding.
-extension _NameMap : Sendable {}
-extension _NameMap.Name : @unchecked Sendable {}
-extension InternPool : @unchecked Sendable {}
+extension _NameMap: Sendable {}
+extension _NameMap.Name: @unchecked Sendable {}
+extension InternPool: @unchecked Sendable {}

+ 404 - 401
Sources/SwiftProtobuf/PathDecoder.swift

@@ -17,424 +17,427 @@ import Foundation
 /// Describes errors can occure during decoding a proto by path.
 public enum PathDecodingError: Error {
 
-  /// Describes a mismatch in type of the fields.
-  ///
-  /// If a value of type A is applied to a path with type B.
-  /// this error will be thrown.
-  case typeMismatch
-
-  /// Describes path is not found in message type.
-  ///
-  /// If a message has no field with the given path this
-  /// error will be thrown.
-  case pathNotFound
+    /// Describes a mismatch in type of the fields.
+    ///
+    /// If a value of type A is applied to a path with type B.
+    /// this error will be thrown.
+    case typeMismatch
+
+    /// Describes path is not found in message type.
+    ///
+    /// If a message has no field with the given path this
+    /// error will be thrown.
+    case pathNotFound
 }
 
 extension Message {
-  static func number(for field: String) -> Int? {
-    guard let type = Self.self as? any _ProtoNameProviding.Type else {
-      return nil
+    static func number(for field: String) -> Int? {
+        guard let type = Self.self as? any _ProtoNameProviding.Type else {
+            return nil
+        }
+        guard
+            let number = Array(field.utf8).withUnsafeBytes({ bytes in
+                type._protobuf_nameMap.number(forProtoName: bytes)
+            })
+        else {
+            return nil
+        }
+        if type._protobuf_nameMap.names(for: number)?.proto.description != field {
+            return nil
+        }
+        return number
     }
-    guard let number = Array(field.utf8).withUnsafeBytes({ bytes in
-      type._protobuf_nameMap.number(forProtoName: bytes)
-    }) else {
-      return nil
-    }
-    if type._protobuf_nameMap.names(for: number)?.proto.description != field {
-      return nil
-    }
-    return number
-  }
 
-  static func name(for field: Int) -> String? {
-    guard let type = Self.self as? any _ProtoNameProviding.Type else {
-      return nil
+    static func name(for field: Int) -> String? {
+        guard let type = Self.self as? any _ProtoNameProviding.Type else {
+            return nil
+        }
+        return type._protobuf_nameMap.names(for: field)?.proto.description
     }
-    return type._protobuf_nameMap.names(for: field)?.proto.description
-  }
 }
 
 // Decoder that set value of a message field by the given path
 struct PathDecoder<T: Message>: Decoder {
 
-  // The value should be set to the path
-  private let value: Any?
-
-  // Field number should be overriden by decoder
-  private var number: Int?
-
-  // The path only including sub-paths
-  private let nextPath: [String]
-
-  // Merge options to be concidered while setting value
-  private let mergeOption: Google_Protobuf_FieldMask.MergeOptions
-
-  private var replaceRepeatedFields: Bool {
-    mergeOption.replaceRepeatedFields
-  }
-
-  init(
-    path: [String],
-    value: Any?,
-    mergeOption: Google_Protobuf_FieldMask.MergeOptions
-  ) throws {
-    if let firstComponent = path.first,
-       let number = T.number(for: firstComponent) {
-      self.number = number
-      self.nextPath = .init(path.dropFirst())
-    } else {
-      throw PathDecodingError.pathNotFound
-    }
-    self.value = value
-    self.mergeOption = mergeOption
-  }
-
-  private func setValue<V>(_ value: inout V, defaultValue: V) throws {
-    if !nextPath.isEmpty {
-      throw PathDecodingError.pathNotFound
-    }
-    if self.value == nil {
-      value = defaultValue
-      return
-    }
-    guard let castedValue = self.value as? V else {
-      throw PathDecodingError.typeMismatch
-    }
-    value = castedValue
-  }
-
-  private func setRepeatedValue<V>(_ value: inout [V]) throws {
-    if !nextPath.isEmpty {
-      throw PathDecodingError.pathNotFound
-    }
-    var castedValue: [V] = []
-    if self.value != nil {
-      guard let v = self.value as? [V] else {
-        throw PathDecodingError.typeMismatch
-      }
-      castedValue = v
-    }
-    if replaceRepeatedFields {
-      value = castedValue
-    } else {
-      value.append(contentsOf: castedValue)
-    }
-  }
-
-  private func setMapValue<K, V>(
-    _ value: inout Dictionary<K, V>
-  ) throws {
-    if !nextPath.isEmpty {
-      throw PathDecodingError.pathNotFound
-    }
-    var castedValue: [K: V] = [:]
-    if self.value != nil {
-      guard let v = self.value as? Dictionary<K, V> else {
-        throw PathDecodingError.typeMismatch
-      }
-      castedValue = v
-    }
-    if replaceRepeatedFields {
-      value = castedValue
-    } else {
-      value.merge(castedValue) { _, new in
-        new
-      }
-    }
-  }
-
-  private func setMessageValue<M: Message>(
-    _ value: inout M?
-  ) throws {
-    if nextPath.isEmpty {
-      try setValue(&value, defaultValue: nil)
-      return
-    }
-    var decoder = try PathDecoder<M>(
-        path: nextPath,
-        value: self.value,
-        mergeOption: mergeOption
-    )
-    if value == nil {
-      value = .init()
-    }
-    try value?.decodeMessage(decoder: &decoder)
-  }
-
-  mutating func handleConflictingOneOf() throws {}
-
-  mutating func nextFieldNumber() throws -> Int? {
-    defer { number = nil }
-    return number
-  }
-
-  mutating func decodeSingularFloatField(value: inout Float) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularFloatField(value: inout Float?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedFloatField(value: inout [Float]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularDoubleField(value: inout Double) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularDoubleField(value: inout Double?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedDoubleField(value: inout [Double]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularInt32Field(value: inout Int32) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularInt32Field(value: inout Int32?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularInt64Field(value: inout Int64) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularInt64Field(value: inout Int64?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularUInt32Field(value: inout UInt32) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularUInt32Field(value: inout UInt32?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularUInt64Field(value: inout UInt64) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularUInt64Field(value: inout UInt64?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws {
-    try setRepeatedValue(&value)
-  }
+    // The value should be set to the path
+    private let value: Any?
 
-  mutating func decodeSingularSInt32Field(value: inout Int32) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularSInt32Field(value: inout Int32?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
+    // Field number should be overriden by decoder
+    private var number: Int?
 
-  mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularSInt64Field(value: inout Int64) throws {
-    try setValue(&value, defaultValue: .init())
-  }
+    // The path only including sub-paths
+    private let nextPath: [String]
 
-  mutating func decodeSingularSInt64Field(value: inout Int64?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
+    // Merge options to be concidered while setting value
+    private let mergeOption: Google_Protobuf_FieldMask.MergeOptions
 
-  mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularFixed32Field(value: inout UInt32) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularFixed32Field(value: inout UInt32?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularFixed64Field(value: inout UInt64) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularFixed64Field(value: inout UInt64?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularSFixed32Field(value: inout Int32) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularSFixed32Field(value: inout Int32?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularSFixed64Field(value: inout Int64) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularSFixed64Field(value: inout Int64?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularBoolField(value: inout Bool) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularBoolField(value: inout Bool?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedBoolField(value: inout [Bool]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularStringField(value: inout String) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularStringField(value: inout String?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedStringField(value: inout [String]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularBytesField(value: inout Data) throws {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularBytesField(value: inout Data?) throws {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedBytesField(value: inout [Data]) throws {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularEnumField<E>(
-    value: inout E
-  ) throws where E : Enum, E.RawValue == Int {
-    try setValue(&value, defaultValue: .init())
-  }
-
-  mutating func decodeSingularEnumField<E>(
-    value: inout E?
-  ) throws where E : Enum, E.RawValue == Int {
-    try setValue(&value, defaultValue: nil)
-  }
-
-  mutating func decodeRepeatedEnumField<E>(
-    value: inout [E]
-  ) throws where E : Enum, E.RawValue == Int {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularMessageField<M>(
-    value: inout M?
-  ) throws where M : Message {
-    try setMessageValue(&value)
-  }
-
-  mutating func decodeRepeatedMessageField<M>(
-    value: inout [M]
-  ) throws where M : Message {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeSingularGroupField<G>(
-    value: inout G?
-  ) throws where G : Message {
-    try setMessageValue(&value)
-  }
-
-  mutating func decodeRepeatedGroupField<G>(
-    value: inout [G]
-  ) throws where G : Message {
-    try setRepeatedValue(&value)
-  }
-
-  mutating func decodeMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: inout _ProtobufMap<KeyType, ValueType>.BaseType
-  ) throws where KeyType : MapKeyType, ValueType : MapValueType {
-    try setMapValue(&value)
-  }
-
-  mutating func decodeMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
-  ) throws where KeyType : MapKeyType, ValueType : Enum, ValueType.RawValue == Int {
-    try setMapValue(&value)
-  }
-
-  mutating func decodeMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
-  ) throws where KeyType : MapKeyType, ValueType : Hashable, ValueType : Message {
-    try setMapValue(&value)
-  }
-
-  mutating func decodeExtensionField(
-    values: inout ExtensionFieldValueSet,
-    messageType: any Message.Type,
-    fieldNumber: Int
-  ) throws {
-    preconditionFailure(
-      "Internal Error: Path decoder should never decode an extension field"
-    )
-  }
+    private var replaceRepeatedFields: Bool {
+        mergeOption.replaceRepeatedFields
+    }
+
+    init(
+        path: [String],
+        value: Any?,
+        mergeOption: Google_Protobuf_FieldMask.MergeOptions
+    ) throws {
+        if let firstComponent = path.first,
+            let number = T.number(for: firstComponent)
+        {
+            self.number = number
+            self.nextPath = .init(path.dropFirst())
+        } else {
+            throw PathDecodingError.pathNotFound
+        }
+        self.value = value
+        self.mergeOption = mergeOption
+    }
+
+    private func setValue<V>(_ value: inout V, defaultValue: V) throws {
+        if !nextPath.isEmpty {
+            throw PathDecodingError.pathNotFound
+        }
+        if self.value == nil {
+            value = defaultValue
+            return
+        }
+        guard let castedValue = self.value as? V else {
+            throw PathDecodingError.typeMismatch
+        }
+        value = castedValue
+    }
+
+    private func setRepeatedValue<V>(_ value: inout [V]) throws {
+        if !nextPath.isEmpty {
+            throw PathDecodingError.pathNotFound
+        }
+        var castedValue: [V] = []
+        if self.value != nil {
+            guard let v = self.value as? [V] else {
+                throw PathDecodingError.typeMismatch
+            }
+            castedValue = v
+        }
+        if replaceRepeatedFields {
+            value = castedValue
+        } else {
+            value.append(contentsOf: castedValue)
+        }
+    }
+
+    private func setMapValue<K, V>(
+        _ value: inout [K: V]
+    ) throws {
+        if !nextPath.isEmpty {
+            throw PathDecodingError.pathNotFound
+        }
+        var castedValue: [K: V] = [:]
+        if self.value != nil {
+            guard let v = self.value as? [K: V] else {
+                throw PathDecodingError.typeMismatch
+            }
+            castedValue = v
+        }
+        if replaceRepeatedFields {
+            value = castedValue
+        } else {
+            value.merge(castedValue) { _, new in
+                new
+            }
+        }
+    }
+
+    private func setMessageValue<M: Message>(
+        _ value: inout M?
+    ) throws {
+        if nextPath.isEmpty {
+            try setValue(&value, defaultValue: nil)
+            return
+        }
+        var decoder = try PathDecoder<M>(
+            path: nextPath,
+            value: self.value,
+            mergeOption: mergeOption
+        )
+        if value == nil {
+            value = .init()
+        }
+        try value?.decodeMessage(decoder: &decoder)
+    }
+
+    mutating func handleConflictingOneOf() throws {}
+
+    mutating func nextFieldNumber() throws -> Int? {
+        defer { number = nil }
+        return number
+    }
+
+    mutating func decodeSingularFloatField(value: inout Float) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularFloatField(value: inout Float?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedFloatField(value: inout [Float]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularDoubleField(value: inout Double) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularDoubleField(value: inout Double?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedDoubleField(value: inout [Double]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularInt32Field(value: inout Int32) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularInt32Field(value: inout Int32?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularInt64Field(value: inout Int64) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularInt64Field(value: inout Int64?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularUInt32Field(value: inout UInt32) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularUInt32Field(value: inout UInt32?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularUInt64Field(value: inout UInt64) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularUInt64Field(value: inout UInt64?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularSInt32Field(value: inout Int32) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularSInt32Field(value: inout Int32?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularSInt64Field(value: inout Int64) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularSInt64Field(value: inout Int64?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularFixed32Field(value: inout UInt32) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularFixed32Field(value: inout UInt32?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularFixed64Field(value: inout UInt64) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularFixed64Field(value: inout UInt64?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularSFixed32Field(value: inout Int32) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularSFixed32Field(value: inout Int32?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularSFixed64Field(value: inout Int64) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularSFixed64Field(value: inout Int64?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularBoolField(value: inout Bool) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularBoolField(value: inout Bool?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedBoolField(value: inout [Bool]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularStringField(value: inout String) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularStringField(value: inout String?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedStringField(value: inout [String]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularBytesField(value: inout Data) throws {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularBytesField(value: inout Data?) throws {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedBytesField(value: inout [Data]) throws {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularEnumField<E>(
+        value: inout E
+    ) throws where E: Enum, E.RawValue == Int {
+        try setValue(&value, defaultValue: .init())
+    }
+
+    mutating func decodeSingularEnumField<E>(
+        value: inout E?
+    ) throws where E: Enum, E.RawValue == Int {
+        try setValue(&value, defaultValue: nil)
+    }
+
+    mutating func decodeRepeatedEnumField<E>(
+        value: inout [E]
+    ) throws where E: Enum, E.RawValue == Int {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularMessageField<M>(
+        value: inout M?
+    ) throws where M: Message {
+        try setMessageValue(&value)
+    }
+
+    mutating func decodeRepeatedMessageField<M>(
+        value: inout [M]
+    ) throws where M: Message {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeSingularGroupField<G>(
+        value: inout G?
+    ) throws where G: Message {
+        try setMessageValue(&value)
+    }
+
+    mutating func decodeRepeatedGroupField<G>(
+        value: inout [G]
+    ) throws where G: Message {
+        try setRepeatedValue(&value)
+    }
+
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMap<KeyType, ValueType>.BaseType
+    ) throws where KeyType: MapKeyType, ValueType: MapValueType {
+        try setMapValue(&value)
+    }
+
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
+    ) throws where KeyType: MapKeyType, ValueType: Enum, ValueType.RawValue == Int {
+        try setMapValue(&value)
+    }
+
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
+    ) throws where KeyType: MapKeyType, ValueType: Hashable, ValueType: Message {
+        try setMapValue(&value)
+    }
+
+    mutating func decodeExtensionField(
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type,
+        fieldNumber: Int
+    ) throws {
+        preconditionFailure(
+            "Internal Error: Path decoder should never decode an extension field"
+        )
+    }
 
 }
 
 extension Message {
-  mutating func `set`(
-    path: String,
-    value: Any?,
-    mergeOption: Google_Protobuf_FieldMask.MergeOptions
-  ) throws {
-    let _path = path.components(separatedBy: ".")
-    var decoder = try PathDecoder<Self>(
-      path: _path,
-      value: value,
-      mergeOption: mergeOption
-    )
-    try decodeMessage(decoder: &decoder)
-  }
+    mutating func `set`(
+        path: String,
+        value: Any?,
+        mergeOption: Google_Protobuf_FieldMask.MergeOptions
+    ) throws {
+        let _path = path.components(separatedBy: ".")
+        var decoder = try PathDecoder<Self>(
+            path: _path,
+            value: value,
+            mergeOption: mergeOption
+        )
+        try decodeMessage(decoder: &decoder)
+    }
 }

+ 226 - 226
Sources/SwiftProtobuf/PathVisitor.swift

@@ -17,268 +17,268 @@ import Foundation
 // Visitor captures all values of message with their paths
 struct PathVisitor<T: Message>: Visitor {
 
-  // The path contains parent components
-  private let prevPath: String?
+    // The path contains parent components
+    private let prevPath: String?
 
-  // Captured values after visiting will be stored in this property
-  private(set) var values: [String: Any] = [:]
+    // Captured values after visiting will be stored in this property
+    private(set) var values: [String: Any] = [:]
 
-  internal init(prevPath: String? = nil) {
-    self.prevPath = prevPath
-  }
+    internal init(prevPath: String? = nil) {
+        self.prevPath = prevPath
+    }
+
+    mutating private func visit(_ value: Any, fieldNumber: Int) {
+        guard let name = T.name(for: fieldNumber) else {
+            return
+        }
+        if let prevPath {
+            values["\(prevPath).\(name)"] = value
+        } else {
+            values[name] = value
+        }
+    }
+
+    mutating private func visitMessageField<M: Message>(
+        _ value: M,
+        fieldNumber: Int
+    ) {
+        guard var path = T.name(for: fieldNumber) else {
+            return
+        }
+        if let prevPath {
+            path = "\(prevPath).\(path)"
+        }
+        values[path] = value
+        var visitor = PathVisitor<M>(prevPath: path)
+        try? value.traverse(visitor: &visitor)
+        values.merge(visitor.values) { _, new in
+            new
+        }
+    }
+
+    mutating func visitUnknown(bytes: Data) throws {}
+
+    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating private func visit(_ value: Any, fieldNumber: Int) {
-    guard let name = T.name(for: fieldNumber) else {
-      return
+    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
+        visitMessageField(value, fieldNumber: fieldNumber)
     }
-    if let prevPath {
-      values["\(prevPath).\(name)"] = value
-    } else {
-      values[name] = value
+
+    mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
+        visitMessageField(value, fieldNumber: fieldNumber)
     }
-  }
 
-  mutating private func visitMessageField<M: Message>(
-    _ value: M,
-    fieldNumber: Int
-  ) {
-    guard var path = T.name(for: fieldNumber) else {
-      return
+    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
     }
-    if let prevPath {
-      path = "\(prevPath).\(path)"
+
+    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
     }
-    values[path] = value
-    var visitor = PathVisitor<M>(prevPath: path)
-    try? value.traverse(visitor: &visitor)
-    values.merge(visitor.values) { _, new in
-      new
+
+    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
     }
-  }
 
-  mutating func visitUnknown(bytes: Data) throws {}
+    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedMessageField<M>(value: [M], fieldNumber: Int) throws where M: Message {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
-    visitMessageField(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
-    visitMessageField(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where KeyType: MapKeyType, ValueType: MapValueType {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where KeyType: MapKeyType, ValueType: Enum, ValueType.RawValue == Int {
+        visit(value, fieldNumber: fieldNumber)
+    }
 
-  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedMessageField<M>(value: [M], fieldNumber: Int) throws where M : Message {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: _ProtobufMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where KeyType : MapKeyType, ValueType : MapValueType {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where KeyType : MapKeyType, ValueType : Enum, ValueType.RawValue == Int {
-    visit(value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where KeyType : MapKeyType, ValueType : Hashable, ValueType : Message {
-    visit(value, fieldNumber: fieldNumber)
-  }
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where KeyType: MapKeyType, ValueType: Hashable, ValueType: Message {
+        visit(value, fieldNumber: fieldNumber)
+    }
 }

+ 3 - 4
Sources/SwiftProtobuf/ProtoNameProviding.swift

@@ -8,7 +8,6 @@
 //
 // -----------------------------------------------------------------------------
 
-
 /// SwiftProtobuf Internal: Common support looking up field names.
 ///
 /// Messages conform to this protocol to provide the proto/text and JSON field
@@ -17,7 +16,7 @@
 /// (reducing bloat and minimizing leaks of field names).
 public protocol _ProtoNameProviding {
 
-  /// The mapping between field numbers and proto/JSON field names defined in
-  /// the conforming message type.
-  static var _protobuf_nameMap: _NameMap { get }
+    /// The mapping between field numbers and proto/JSON field names defined in
+    /// the conforming message type.
+    static var _protobuf_nameMap: _NameMap { get }
 }

+ 1 - 2
Sources/SwiftProtobuf/ProtobufAPIVersionCheck.swift

@@ -19,7 +19,6 @@
 ///
 // -----------------------------------------------------------------------------
 
-
 /// An empty protocol that encodes the version of the runtime library.
 ///
 /// This protocol will be replaced with one containing a different version
@@ -39,5 +38,5 @@ public protocol ProtobufAPIVersion_2 {}
 /// causing a compile-time error (with reasonable diagnostics) if they are
 /// incompatible.
 public protocol ProtobufAPIVersionCheck {
-  associatedtype Version: ProtobufAPIVersion_2
+    associatedtype Version: ProtobufAPIVersion_2
 }

+ 6 - 9
Sources/SwiftProtobuf/ProtobufMap.swift

@@ -15,25 +15,22 @@
 import Foundation
 
 /// SwiftProtobuf Internal: Support for Encoding/Decoding.
-public struct _ProtobufMap<KeyType: MapKeyType, ValueType: FieldType>
-{
+public struct _ProtobufMap<KeyType: MapKeyType, ValueType: FieldType> {
     public typealias Key = KeyType.BaseType
     public typealias Value = ValueType.BaseType
-    public typealias BaseType = Dictionary<Key, Value>
+    public typealias BaseType = [Key: Value]
 }
 
 /// SwiftProtobuf Internal: Support for Encoding/Decoding.
-public struct _ProtobufMessageMap<KeyType: MapKeyType, ValueType: Message & Hashable>
-{
+public struct _ProtobufMessageMap<KeyType: MapKeyType, ValueType: Message & Hashable> {
     public typealias Key = KeyType.BaseType
     public typealias Value = ValueType
-    public typealias BaseType = Dictionary<Key, Value>
+    public typealias BaseType = [Key: Value]
 }
 
 /// SwiftProtobuf Internal: Support for Encoding/Decoding.
-public struct _ProtobufEnumMap<KeyType: MapKeyType, ValueType: Enum>
-{
+public struct _ProtobufEnumMap<KeyType: MapKeyType, ValueType: Enum> {
     public typealias Key = KeyType.BaseType
     public typealias Value = ValueType
-    public typealias BaseType = Dictionary<Key, Value>
+    public typealias BaseType = [Key: Value]
 }

+ 213 - 212
Sources/SwiftProtobuf/SelectiveVisitor.swift

@@ -16,7 +16,7 @@ import Foundation
 
 /// A base for Visitors that only expects a subset of things to called.
 internal protocol SelectiveVisitor: Visitor {
-  // Adds nothing.
+    // Adds nothing.
 }
 
 /// Default impls for everything so things using this only have to write the
@@ -27,242 +27,243 @@ internal protocol SelectiveVisitor: Visitor {
 /// provided by Visitor to bridge packed->repeated, repeated->singular, etc
 /// won't kick in.
 extension SelectiveVisitor {
-  internal mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(false)
+    }
 
-  internal mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitMapField<KeyType, ValueType: MapValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: _ProtobufMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where ValueType.RawValue == Int {
-    assert(false)
-  }
-
-  internal mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-    assert(false)
-  }
-
-  internal mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws {
-    assert(false)
-  }
-
-  internal mutating func visitExtensionFieldsAsMessageSet(
-    fields: ExtensionFieldValueSet,
-    start: Int,
-    end: Int
-  ) throws {
-    assert(false)
-  }
-
-  internal mutating func visitUnknown(bytes: Data) throws {
-    assert(false)
-  }
+    internal mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        assert(false)
+    }
+
+    internal mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where ValueType.RawValue == Int {
+        assert(false)
+    }
+
+    internal mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        assert(false)
+    }
+
+    internal mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws {
+        assert(false)
+    }
+
+    internal mutating func visitExtensionFieldsAsMessageSet(
+        fields: ExtensionFieldValueSet,
+        start: Int,
+        end: Int
+    ) throws {
+        assert(false)
+    }
+
+    internal mutating func visitUnknown(bytes: Data) throws {
+        assert(false)
+    }
 }

+ 4 - 5
Sources/SwiftProtobuf/SimpleExtensionMap.swift

@@ -12,13 +12,12 @@
 ///
 // -----------------------------------------------------------------------------
 
-
 // Note: The generated code only relies on ExpressibleByArrayLiteral
 public struct SimpleExtensionMap: ExtensionMap, ExpressibleByArrayLiteral {
     public typealias Element = AnyMessageExtension
 
     // Since type objects aren't Hashable, we can't do much better than this...
-    internal var fields = [Int: Array<any AnyMessageExtension>]()
+    internal var fields = [Int: [any AnyMessageExtension]]()
 
     public init() {}
 
@@ -27,9 +26,9 @@ public struct SimpleExtensionMap: ExtensionMap, ExpressibleByArrayLiteral {
     }
 
     public init(_ others: SimpleExtensionMap...) {
-      for other in others {
-        formUnion(other)
-      }
+        for other in others {
+            formUnion(other)
+        }
     }
 
     public subscript(messageType: any Message.Type, fieldNumber: Int) -> (any AnyMessageExtension)? {

+ 39 - 41
Sources/SwiftProtobuf/StringUtils.swift

@@ -26,43 +26,42 @@ import Foundation
 // Note: We're trying to avoid Foundation's String(format:) since that's not
 // universally available.
 
-fileprivate func formatZeroPaddedInt(_ value: Int32, digits: Int) -> String {
-  precondition(value >= 0)
-  let s = String(value)
-  if s.count >= digits {
-    return s
-  } else {
-    let pad = String(repeating: "0", count: digits - s.count)
-    return pad + s
-  }
+private func formatZeroPaddedInt(_ value: Int32, digits: Int) -> String {
+    precondition(value >= 0)
+    let s = String(value)
+    if s.count >= digits {
+        return s
+    } else {
+        let pad = String(repeating: "0", count: digits - s.count)
+        return pad + s
+    }
 }
 
 internal func twoDigit(_ value: Int32) -> String {
-  return formatZeroPaddedInt(value, digits: 2)
+    formatZeroPaddedInt(value, digits: 2)
 }
 internal func threeDigit(_ value: Int32) -> String {
-  return formatZeroPaddedInt(value, digits: 3)
+    formatZeroPaddedInt(value, digits: 3)
 }
 internal func fourDigit(_ value: Int32) -> String {
-  return formatZeroPaddedInt(value, digits: 4)
+    formatZeroPaddedInt(value, digits: 4)
 }
 internal func sixDigit(_ value: Int32) -> String {
-  return formatZeroPaddedInt(value, digits: 6)
+    formatZeroPaddedInt(value, digits: 6)
 }
 internal func nineDigit(_ value: Int32) -> String {
-  return formatZeroPaddedInt(value, digits: 9)
+    formatZeroPaddedInt(value, digits: 9)
 }
 
 // Wrapper that takes a buffer and start/end offsets
 internal func utf8ToString(
-  bytes: UnsafeRawBufferPointer,
-  start: UnsafeRawBufferPointer.Index,
-  end: UnsafeRawBufferPointer.Index
+    bytes: UnsafeRawBufferPointer,
+    start: UnsafeRawBufferPointer.Index,
+    end: UnsafeRawBufferPointer.Index
 ) -> String? {
-  return utf8ToString(bytes: bytes.baseAddress! + start, count: end - start)
+    utf8ToString(bytes: bytes.baseAddress! + start, count: end - start)
 }
 
-
 // Swift 4 introduced new faster String facilities
 // that seem to work consistently across all platforms.
 
@@ -79,28 +78,27 @@ internal func utf8ToString(
 // slower than on macOS, so this is a much bigger
 // win there.
 internal func utf8ToString(bytes: UnsafeRawPointer, count: Int) -> String? {
-  if count == 0 {
-    return String()
-  }
-  let codeUnits = UnsafeRawBufferPointer(start: bytes, count: count)
-  let sourceEncoding = Unicode.UTF8.self
+    if count == 0 {
+        return String()
+    }
+    let codeUnits = UnsafeRawBufferPointer(start: bytes, count: count)
+    let sourceEncoding = Unicode.UTF8.self
 
-  // Verify that the UTF-8 is valid.
-  var p = sourceEncoding.ForwardParser()
-  var i = codeUnits.makeIterator()
-  Loop:
-  while true {
-    switch p.parseScalar(from: &i) {
-    case .valid(_):
-      break
-    case .error:
-      return nil
-    case .emptyInput:
-      break Loop
+    // Verify that the UTF-8 is valid.
+    var p = sourceEncoding.ForwardParser()
+    var i = codeUnits.makeIterator()
+    Loop: while true {
+        switch p.parseScalar(from: &i) {
+        case .valid(_):
+            break
+        case .error:
+            return nil
+        case .emptyInput:
+            break Loop
+        }
     }
-  }
 
-  // This initializer is fast but does not reject broken
-  // UTF-8 (which is why we validate the UTF-8 above).
-  return String(decoding: codeUnits, as: sourceEncoding)
- }
+    // This initializer is fast but does not reject broken
+    // UTF-8 (which is why we validate the UTF-8 above).
+    return String(decoding: codeUnits, as: sourceEncoding)
+}

+ 1 - 1
Sources/SwiftProtobuf/SwiftProtobufContiguousBytes.swift

@@ -14,7 +14,7 @@ import Foundation
 /// to be used for serialization and deserialization of protobufs.
 /// It provides a general interface for bytes since the Swift Standard Library currently does not
 /// provide such a protocol.
-///  
+///
 /// By conforming your own types to this protocol, you will be able to pass instances of said types
 /// directly to `SwiftProtobuf.Message`'s deserialisation methods
 /// (i.e. `init(contiguousBytes:)` for binary format and `init(jsonUTF8Bytes:)` for JSON).

+ 59 - 59
Sources/SwiftProtobuf/SwiftProtobufError.swift

@@ -25,7 +25,7 @@ public struct SwiftProtobufError: Error, @unchecked Sendable {
             self.storage = self.storage.copy()
         }
     }
-    
+
     private final class Storage {
         var code: Code
         var message: String
@@ -42,14 +42,14 @@ public struct SwiftProtobufError: Error, @unchecked Sendable {
         }
 
         func copy() -> Self {
-            return Self(
+            Self(
                 code: self.code,
                 message: self.message,
                 location: self.location
             )
         }
     }
-    
+
     /// A high-level error code to provide broad a classification.
     public var code: Code {
         get { self.storage.code }
@@ -67,7 +67,7 @@ public struct SwiftProtobufError: Error, @unchecked Sendable {
             self.storage.message = newValue
         }
     }
-    
+
     private var location: SourceLocation {
         get { self.storage.location }
         set {
@@ -111,12 +111,12 @@ extension SwiftProtobufError {
         private init(_ code: Wrapped) {
             self.code = code
         }
-        
+
         /// Errors arising from binary decoding of data into protobufs.
         public static var binaryDecodingError: Self {
             Self(.binaryDecodingError)
         }
-        
+
         /// Errors arising from decoding streams of binary messages. These errors have to do with the framing
         /// of the messages in the stream, or the stream as a whole.
         public static var binaryStreamDecodingError: Self {
@@ -147,7 +147,7 @@ extension SwiftProtobufError {
             file: String = #fileID,
             line: Int = #line
         ) -> Self {
-            return SourceLocation(function: function, file: file, line: line)
+            SourceLocation(function: function, file: file, line: line)
         }
     }
 }
@@ -182,60 +182,60 @@ extension SwiftProtobufError {
             )
         }
     }
-    
+
     /// Errors arising from decoding streams of binary messages. These errors have to do with the framing
     /// of the messages in the stream, or the stream as a whole.
     public enum BinaryStreamDecoding {
-      /// Message is too large. Bytes and Strings have a max size of 2GB.
-      public static func tooLarge(
-          function: String = #function,
-          file: String = #fileID,
-          line: Int = #line
-      ) -> SwiftProtobufError {
-          SwiftProtobufError(
-              code: .binaryStreamDecodingError,
-              message: "Message too large: Bytes and Strings have a max size of 2GB.",
-              location: SourceLocation(function: function, file: file, line: line)
-          )
-      }
-      
-      /// While attempting to read the length of a message on the stream, the
-      /// bytes were malformed for the protobuf format.
-      public static func malformedLength(
-        function: String = #function,
-        file: String = #fileID,
-        line: Int = #line
-      ) -> SwiftProtobufError {
-        SwiftProtobufError(
-          code: .binaryStreamDecodingError,
-          message: """
-          While attempting to read the length of a binary-delimited message \
-          on the stream, the bytes were malformed for the protobuf format.
-        """,
-          location: .init(function: function, file: file, line: line)
-        )
-      }
-      
-      /// This isn't really an error. `InputStream` documents that
-      /// `hasBytesAvailable` _may_ return `True` if a read is needed to
-      /// determine if there really are bytes available. So this "error" is thrown
-      /// when a `parse` or `merge` fails because there were no bytes available.
-      /// If this is raised, the callers should decide via what ever other means
-      /// are correct if the stream has completely ended or if more bytes might
-      /// eventually show up.
-      public static func noBytesAvailable(
-        function: String = #function,
-        file: String = #fileID,
-        line: Int = #line
-      ) -> SwiftProtobufError {
-        SwiftProtobufError(
-          code: .binaryStreamDecodingError,
-          message: """
-          This is not really an error: please read the documentation for
-          `SwiftProtobufError/BinaryStreamDecoding/noBytesAvailable` for more information.
-        """,
-          location: .init(function: function, file: file, line: line)
-        )
-      }
+        /// Message is too large. Bytes and Strings have a max size of 2GB.
+        public static func tooLarge(
+            function: String = #function,
+            file: String = #fileID,
+            line: Int = #line
+        ) -> SwiftProtobufError {
+            SwiftProtobufError(
+                code: .binaryStreamDecodingError,
+                message: "Message too large: Bytes and Strings have a max size of 2GB.",
+                location: SourceLocation(function: function, file: file, line: line)
+            )
+        }
+
+        /// While attempting to read the length of a message on the stream, the
+        /// bytes were malformed for the protobuf format.
+        public static func malformedLength(
+            function: String = #function,
+            file: String = #fileID,
+            line: Int = #line
+        ) -> SwiftProtobufError {
+            SwiftProtobufError(
+                code: .binaryStreamDecodingError,
+                message: """
+                      While attempting to read the length of a binary-delimited message \
+                      on the stream, the bytes were malformed for the protobuf format.
+                    """,
+                location: .init(function: function, file: file, line: line)
+            )
+        }
+
+        /// This isn't really an error. `InputStream` documents that
+        /// `hasBytesAvailable` _may_ return `True` if a read is needed to
+        /// determine if there really are bytes available. So this "error" is thrown
+        /// when a `parse` or `merge` fails because there were no bytes available.
+        /// If this is raised, the callers should decide via what ever other means
+        /// are correct if the stream has completely ended or if more bytes might
+        /// eventually show up.
+        public static func noBytesAvailable(
+            function: String = #function,
+            file: String = #fileID,
+            line: Int = #line
+        ) -> SwiftProtobufError {
+            SwiftProtobufError(
+                code: .binaryStreamDecodingError,
+                message: """
+                      This is not really an error: please read the documentation for
+                      `SwiftProtobufError/BinaryStreamDecoding/noBytesAvailable` for more information.
+                    """,
+                location: .init(function: function, file: file, line: line)
+            )
+        }
     }
 }

+ 41 - 17
Sources/SwiftProtobuf/TextFormatDecoder.swift

@@ -28,21 +28,21 @@ internal struct TextFormatDecoder: Decoder {
     private var messageType: any Message.Type
 
     internal var options: TextFormatDecodingOptions {
-      return scanner.options
+        scanner.options
     }
 
     internal var complete: Bool {
         mutating get {
-            return scanner.complete
+            scanner.complete
         }
     }
 
     internal init(
-      messageType: any Message.Type,
-      utf8Pointer: UnsafeRawPointer,
-      count: Int,
-      options: TextFormatDecodingOptions,
-      extensions: (any ExtensionMap)?
+        messageType: any Message.Type,
+        utf8Pointer: UnsafeRawPointer,
+        count: Int,
+        options: TextFormatDecodingOptions,
+        extensions: (any ExtensionMap)?
     ) throws {
         scanner = TextFormatScanner(utf8Pointer: utf8Pointer, count: count, options: options, extensions: extensions)
         guard let nameProviding = (messageType as? any _ProtoNameProviding.Type) else {
@@ -70,9 +70,11 @@ internal struct TextFormatDecoder: Decoder {
         if fieldCount > 0 {
             scanner.skipOptionalSeparator()
         }
-        if let fieldNumber = try scanner.nextFieldNumber(names: fieldNameMap!,
-                                                         messageType: messageType,
-                                                         terminator: terminator) {
+        if let fieldNumber = try scanner.nextFieldNumber(
+            names: fieldNameMap!,
+            messageType: messageType,
+            terminator: terminator
+        ) {
             fieldCount += 1
             return fieldNumber
         } else {
@@ -549,7 +551,10 @@ internal struct TextFormatDecoder: Decoder {
         try decodeRepeatedMessageField(value: &value)
     }
 
-    private mutating func decodeMapEntry<KeyType, ValueType: MapValueType>(mapType: _ProtobufMap<KeyType, ValueType>.Type, value: inout _ProtobufMap<KeyType, ValueType>.BaseType) throws {
+    private mutating func decodeMapEntry<KeyType, ValueType: MapValueType>(
+        mapType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMap<KeyType, ValueType>.BaseType
+    ) throws {
         var keyField: KeyType.BaseType?
         var valueField: ValueType.BaseType?
         let terminator = try scanner.skipObjectStart()
@@ -585,7 +590,10 @@ internal struct TextFormatDecoder: Decoder {
         }
     }
 
-    mutating func decodeMapField<KeyType, ValueType: MapValueType>(fieldType: _ProtobufMap<KeyType, ValueType>.Type, value: inout _ProtobufMap<KeyType, ValueType>.BaseType) throws {
+    mutating func decodeMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMap<KeyType, ValueType>.BaseType
+    ) throws {
         _ = scanner.skipOptionalColon()
         if scanner.skipOptionalBeginArray() {
             var firstItem = true
@@ -605,7 +613,10 @@ internal struct TextFormatDecoder: Decoder {
         }
     }
 
-    private mutating func decodeMapEntry<KeyType, ValueType>(mapType: _ProtobufEnumMap<KeyType, ValueType>.Type, value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType) throws where ValueType.RawValue == Int {
+    private mutating func decodeMapEntry<KeyType, ValueType>(
+        mapType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
+    ) throws where ValueType.RawValue == Int {
         var keyField: KeyType.BaseType?
         var valueField: ValueType?
         let terminator = try scanner.skipObjectStart()
@@ -641,7 +652,10 @@ internal struct TextFormatDecoder: Decoder {
         }
     }
 
-    mutating func decodeMapField<KeyType, ValueType>(fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type, value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType) throws where ValueType.RawValue == Int {
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
+    ) throws where ValueType.RawValue == Int {
         _ = scanner.skipOptionalColon()
         if scanner.skipOptionalBeginArray() {
             var firstItem = true
@@ -661,7 +675,10 @@ internal struct TextFormatDecoder: Decoder {
         }
     }
 
-    private mutating func decodeMapEntry<KeyType, ValueType>(mapType: _ProtobufMessageMap<KeyType, ValueType>.Type, value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType) throws {
+    private mutating func decodeMapEntry<KeyType, ValueType>(
+        mapType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
+    ) throws {
         var keyField: KeyType.BaseType?
         var valueField: ValueType?
         let terminator = try scanner.skipObjectStart()
@@ -697,7 +714,10 @@ internal struct TextFormatDecoder: Decoder {
         }
     }
 
-    mutating func decodeMapField<KeyType, ValueType>(fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type, value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType) throws {
+    mutating func decodeMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
+    ) throws {
         _ = scanner.skipOptionalColon()
         if scanner.skipOptionalBeginArray() {
             var firstItem = true
@@ -717,7 +737,11 @@ internal struct TextFormatDecoder: Decoder {
         }
     }
 
-    mutating func decodeExtensionField(values: inout ExtensionFieldValueSet, messageType: any Message.Type, fieldNumber: Int) throws {
+    mutating func decodeExtensionField(
+        values: inout ExtensionFieldValueSet,
+        messageType: any Message.Type,
+        fieldNumber: Int
+    ) throws {
         if let ext = scanner.extensions?[messageType, fieldNumber] {
             try values.modify(index: fieldNumber) { fieldValue in
                 if fieldValue != nil {

+ 19 - 19
Sources/SwiftProtobuf/TextFormatDecodingOptions.swift

@@ -14,26 +14,26 @@
 
 /// Options for TextFormatDecoding.
 public struct TextFormatDecodingOptions: Sendable {
-  /// The maximum nesting of message with messages.  The default is 100.
-  ///
-  /// To prevent corrupt or malicious messages from causing stack overflows,
-  /// this controls how deep messages can be nested within other messages
-  /// while parsing.
-  public var messageDepthLimit: Int = 100
+    /// The maximum nesting of message with messages.  The default is 100.
+    ///
+    /// To prevent corrupt or malicious messages from causing stack overflows,
+    /// this controls how deep messages can be nested within other messages
+    /// while parsing.
+    public var messageDepthLimit: Int = 100
 
-  /// If unknown fields in the TextFormat should be ignored. If they aren't
-  /// ignored, an error will be raised if one is encountered.
-  ///
-  /// Note: This is a lossy option, enabling it means part of the TextFormat
-  /// is silently skipped.
-  public var ignoreUnknownFields: Bool = false
+    /// If unknown fields in the TextFormat should be ignored. If they aren't
+    /// ignored, an error will be raised if one is encountered.
+    ///
+    /// Note: This is a lossy option, enabling it means part of the TextFormat
+    /// is silently skipped.
+    public var ignoreUnknownFields: Bool = false
 
-  /// If unknown extension fields in the TextFormat should be ignored. If they
-  /// aren't ignored, an error will be raised if one is encountered.
-  ///
-  /// Note: This is a lossy option, enabling it means part of the TextFormat
-  /// is silently skipped.
-  public var ignoreUnknownExtensionFields: Bool = false
+    /// If unknown extension fields in the TextFormat should be ignored. If they
+    /// aren't ignored, an error will be raised if one is encountered.
+    ///
+    /// Note: This is a lossy option, enabling it means part of the TextFormat
+    /// is silently skipped.
+    public var ignoreUnknownExtensionFields: Bool = false
 
-  public init() {}
+    public init() {}
 }

+ 32 - 33
Sources/SwiftProtobuf/TextFormatEncoder.swift

@@ -37,7 +37,7 @@ internal struct TextFormatEncoder {
     private var indentString: [UInt8] = []
     var stringResult: String {
         get {
-            return String(bytes: data, encoding: String.Encoding.utf8)!
+            String(bytes: data, encoding: String.Encoding.utf8)!
         }
     }
 
@@ -231,7 +231,7 @@ internal struct TextFormatEncoder {
                 append(staticText: "\\\"")
             case 92:
                 append(staticText: "\\\\")
-            case 0...31, 127: // Octal form for C0 control chars
+            case 0...31, 127:  // Octal form for C0 control chars
                 data.append(asciiBackslash)
                 data.append(asciiZero + UInt8(c.value / 64))
                 data.append(asciiZero + UInt8(c.value / 8 % 8))
@@ -258,39 +258,38 @@ internal struct TextFormatEncoder {
     mutating func putBytesValue(value: Data) {
         data.append(asciiDoubleQuote)
         value.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
-          if let p = body.baseAddress, body.count > 0 {
-            for i in 0..<body.count {
-              let c = p[i]
-              switch c {
-              // Special two-byte escapes
-              case 8:
-                append(staticText: "\\b")
-              case 9:
-                append(staticText: "\\t")
-              case 10:
-                append(staticText: "\\n")
-              case 11:
-                append(staticText: "\\v")
-              case 12:
-                append(staticText: "\\f")
-              case 13:
-                append(staticText: "\\r")
-              case 34:
-                append(staticText: "\\\"")
-              case 92:
-                append(staticText: "\\\\")
-              case 32...126:  // printable ASCII
-                data.append(c)
-              default: // Octal form for non-printable chars
-                data.append(asciiBackslash)
-                data.append(asciiZero + UInt8(c / 64))
-                data.append(asciiZero + UInt8(c / 8 % 8))
-                data.append(asciiZero + UInt8(c % 8))
-              }
+            if let p = body.baseAddress, body.count > 0 {
+                for i in 0..<body.count {
+                    let c = p[i]
+                    switch c {
+                    // Special two-byte escapes
+                    case 8:
+                        append(staticText: "\\b")
+                    case 9:
+                        append(staticText: "\\t")
+                    case 10:
+                        append(staticText: "\\n")
+                    case 11:
+                        append(staticText: "\\v")
+                    case 12:
+                        append(staticText: "\\f")
+                    case 13:
+                        append(staticText: "\\r")
+                    case 34:
+                        append(staticText: "\\\"")
+                    case 92:
+                        append(staticText: "\\\\")
+                    case 32...126:  // printable ASCII
+                        data.append(c)
+                    default:  // Octal form for non-printable chars
+                        data.append(asciiBackslash)
+                        data.append(asciiZero + UInt8(c / 64))
+                        data.append(asciiZero + UInt8(c / 8 % 8))
+                        data.append(asciiZero + UInt8(c % 8))
+                    }
+                }
             }
-          }
         }
         data.append(asciiDoubleQuote)
     }
 }
-

+ 3 - 3
Sources/SwiftProtobuf/TextFormatEncodingOptions.swift

@@ -15,8 +15,8 @@
 /// Options for TextFormatEncoding.
 public struct TextFormatEncodingOptions: Sendable {
 
-  /// Default: Do print unknown fields using numeric notation
-  public var printUnknownFields: Bool = true
+    /// Default: Do print unknown fields using numeric notation
+    public var printUnknownFields: Bool = true
 
-  public init() {}
+    public init() {}
 }

+ 661 - 648
Sources/SwiftProtobuf/TextFormatEncodingVisitor.swift

@@ -14,659 +14,672 @@
 
 import Foundation
 
-private let mapNameResolver: [Int:StaticString] = [1: "key", 2: "value"]
+private let mapNameResolver: [Int: StaticString] = [1: "key", 2: "value"]
 
 /// Visitor that serializes a message into protobuf text format.
 internal struct TextFormatEncodingVisitor: Visitor {
 
-  private var encoder: TextFormatEncoder
-  private var nameMap: _NameMap?
-  private var nameResolver: [Int:StaticString]
-  private var extensions: ExtensionFieldValueSet?
-  private let options: TextFormatEncodingOptions
-
-  /// The protobuf text produced by the visitor.
-  var result: String {
-    return encoder.stringResult
-  }
-
-  /// Creates a new visitor that serializes the given message to protobuf text
-  /// format.
-  init(message: any Message, options: TextFormatEncodingOptions) {
-    let nameMap: _NameMap?
-    if let nameProviding = message as? (any _ProtoNameProviding) {
-        nameMap = type(of: nameProviding)._protobuf_nameMap
-    } else {
-        nameMap = nil
-    }
-    let extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
-
-    self.nameMap = nameMap
-    self.nameResolver = [:]
-    self.extensions = extensions
-    self.encoder = TextFormatEncoder()
-    self.options = options
-  }
-
-  // TODO: This largely duplicates emitFieldName() below.
-  // But, it's slower so we don't want to just have emitFieldName() use
-  // formatFieldName().  Also, we need to measure whether the optimization
-  // this provides to repeated fields is worth the effort; consider just
-  // removing this and having repeated fields just re-run emitFieldName()
-  // for each item.
-  private func formatFieldName(lookingUp fieldNumber: Int) -> [UInt8] {
-      var bytes = [UInt8]()
-      if let protoName = nameMap?.names(for: fieldNumber)?.proto {
-          bytes.append(contentsOf: protoName.utf8Buffer)
-      } else if let protoName = nameResolver[fieldNumber] {
-          let buff = UnsafeBufferPointer(start: protoName.utf8Start, count: protoName.utf8CodeUnitCount)
-          bytes.append(contentsOf: buff)
-      } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName {
-          bytes.append(UInt8(ascii: "["))
-          bytes.append(contentsOf: extensionName.utf8)
-          bytes.append(UInt8(ascii: "]"))
-      } else {
-          bytes.append(contentsOf: fieldNumber.description.utf8)
-      }
-      return bytes
-  }
-
-  private mutating func emitFieldName(lookingUp fieldNumber: Int) {
-      if let protoName = nameMap?.names(for: fieldNumber)?.proto {
-          encoder.emitFieldName(name: protoName.utf8Buffer)
-      } else if let protoName = nameResolver[fieldNumber] {
-          encoder.emitFieldName(name: protoName)
-      } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName {
-          encoder.emitExtensionFieldName(name: extensionName)
-      } else {
-          encoder.emitFieldNumber(number: fieldNumber)
-      }
-  }
-
-  mutating func visitUnknown(bytes: Data) throws {
-      if options.printUnknownFields {
-          try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> () in
-            if let baseAddress = body.baseAddress, body.count > 0 {
-              // All fields will be directly handled, so there is no need for
-              // the unknown field buffering/collection (when scannings to see
-              // if something is a message, this would be extremely wasteful).
-              var binaryOptions = BinaryDecodingOptions()
-              binaryOptions.discardUnknownFields = true
-              var decoder = BinaryDecoder(forReadingFrom: baseAddress,
-                                          count: body.count,
-                                          options: binaryOptions)
-              try visitUnknown(decoder: &decoder)
+    private var encoder: TextFormatEncoder
+    private var nameMap: _NameMap?
+    private var nameResolver: [Int: StaticString]
+    private var extensions: ExtensionFieldValueSet?
+    private let options: TextFormatEncodingOptions
+
+    /// The protobuf text produced by the visitor.
+    var result: String {
+        encoder.stringResult
+    }
+
+    /// Creates a new visitor that serializes the given message to protobuf text
+    /// format.
+    init(message: any Message, options: TextFormatEncodingOptions) {
+        let nameMap: _NameMap?
+        if let nameProviding = message as? (any _ProtoNameProviding) {
+            nameMap = type(of: nameProviding)._protobuf_nameMap
+        } else {
+            nameMap = nil
+        }
+        let extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
+
+        self.nameMap = nameMap
+        self.nameResolver = [:]
+        self.extensions = extensions
+        self.encoder = TextFormatEncoder()
+        self.options = options
+    }
+
+    // TODO: This largely duplicates emitFieldName() below.
+    // But, it's slower so we don't want to just have emitFieldName() use
+    // formatFieldName().  Also, we need to measure whether the optimization
+    // this provides to repeated fields is worth the effort; consider just
+    // removing this and having repeated fields just re-run emitFieldName()
+    // for each item.
+    private func formatFieldName(lookingUp fieldNumber: Int) -> [UInt8] {
+        var bytes = [UInt8]()
+        if let protoName = nameMap?.names(for: fieldNumber)?.proto {
+            bytes.append(contentsOf: protoName.utf8Buffer)
+        } else if let protoName = nameResolver[fieldNumber] {
+            let buff = UnsafeBufferPointer(start: protoName.utf8Start, count: protoName.utf8CodeUnitCount)
+            bytes.append(contentsOf: buff)
+        } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName {
+            bytes.append(UInt8(ascii: "["))
+            bytes.append(contentsOf: extensionName.utf8)
+            bytes.append(UInt8(ascii: "]"))
+        } else {
+            bytes.append(contentsOf: fieldNumber.description.utf8)
+        }
+        return bytes
+    }
+
+    private mutating func emitFieldName(lookingUp fieldNumber: Int) {
+        if let protoName = nameMap?.names(for: fieldNumber)?.proto {
+            encoder.emitFieldName(name: protoName.utf8Buffer)
+        } else if let protoName = nameResolver[fieldNumber] {
+            encoder.emitFieldName(name: protoName)
+        } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName {
+            encoder.emitExtensionFieldName(name: extensionName)
+        } else {
+            encoder.emitFieldNumber(number: fieldNumber)
+        }
+    }
+
+    mutating func visitUnknown(bytes: Data) throws {
+        if options.printUnknownFields {
+            try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> Void in
+                if let baseAddress = body.baseAddress, body.count > 0 {
+                    // All fields will be directly handled, so there is no need for
+                    // the unknown field buffering/collection (when scannings to see
+                    // if something is a message, this would be extremely wasteful).
+                    var binaryOptions = BinaryDecodingOptions()
+                    binaryOptions.discardUnknownFields = true
+                    var decoder = BinaryDecoder(
+                        forReadingFrom: baseAddress,
+                        count: body.count,
+                        options: binaryOptions
+                    )
+                    try visitUnknown(decoder: &decoder)
+                }
             }
-          }
-      }
-  }
-
-  /// Helper for printing out unknowns.
-  ///
-  /// The implementation tries to be "helpful" and if a length delimited field
-  /// appears to be a submessage, it prints it as such. However, that opens the
-  /// door to someone sending a message with an unknown field that is a stack
-  /// bomb, i.e. - it causes this code to recurse, exhausting the stack and
-  /// thus opening up an attack vector. To keep this "help", but avoid the
-  /// attack, a limit is placed on how many times it will recurse before just
-  /// treating the length delimited fields as bytes and not trying to decode
-  /// them.
-  private mutating func visitUnknown(
-    decoder: inout BinaryDecoder,
-    recursionBudget: Int = 10
-  ) throws {
-      // This stack serves to avoid recursion for groups within groups within
-      // groups..., this avoid the stack attack that the message detection
-      // hits. No limit is placed on this because there is no stack risk with
-      // recursion, and because if a limit was hit, there is no other way to
-      // encode the group (the message field can just print as length
-      // delimited, groups don't have an option like that).
-      var groupFieldNumberStack: [Int] = []
-
-      while let tag = try decoder.getTag() {
-          switch tag.wireFormat {
-          case .varint:
-              encoder.emitFieldNumber(number: tag.fieldNumber)
-              var value: UInt64 = 0
-              encoder.startRegularField()
-              try decoder.decodeSingularUInt64Field(value: &value)
-              encoder.putUInt64(value: value)
-              encoder.endRegularField()
-          case .fixed64:
-              encoder.emitFieldNumber(number: tag.fieldNumber)
-              var value: UInt64 = 0
-              encoder.startRegularField()
-              try decoder.decodeSingularFixed64Field(value: &value)
-              encoder.putUInt64Hex(value: value, digits: 16)
-              encoder.endRegularField()
-          case .lengthDelimited:
-              encoder.emitFieldNumber(number: tag.fieldNumber)
-              var bytes = Data()
-              try decoder.decodeSingularBytesField(value: &bytes)
-              var encodeAsBytes = true
-              if bytes.count > 0 && recursionBudget > 0 {
-                  bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> () in
-                      if let baseAddress = body.baseAddress, body.count > 0 {
-                          do {
-                              // Walk all the fields to test if it looks like a message
-                              var testDecoder = BinaryDecoder(forReadingFrom: baseAddress,
-                                                              count: body.count,
-                                                              parent: decoder)
-                              while let _ = try testDecoder.nextFieldNumber() {
-                              }
-                              // No error?  Output the message body.
-                              encodeAsBytes = false
-                              var subDecoder = BinaryDecoder(forReadingFrom: baseAddress,
-                                                             count: bytes.count,
-                                                             parent: decoder)
-                              encoder.startMessageField()
-                              try visitUnknown(decoder: &subDecoder,
-                                               recursionBudget: recursionBudget - 1)
-                              encoder.endMessageField()
-                          } catch {
-                              encodeAsBytes = true
-                          }
-                      }
-                  }
-              }
-              if (encodeAsBytes) {
+        }
+    }
+
+    /// Helper for printing out unknowns.
+    ///
+    /// The implementation tries to be "helpful" and if a length delimited field
+    /// appears to be a submessage, it prints it as such. However, that opens the
+    /// door to someone sending a message with an unknown field that is a stack
+    /// bomb, i.e. - it causes this code to recurse, exhausting the stack and
+    /// thus opening up an attack vector. To keep this "help", but avoid the
+    /// attack, a limit is placed on how many times it will recurse before just
+    /// treating the length delimited fields as bytes and not trying to decode
+    /// them.
+    private mutating func visitUnknown(
+        decoder: inout BinaryDecoder,
+        recursionBudget: Int = 10
+    ) throws {
+        // This stack serves to avoid recursion for groups within groups within
+        // groups..., this avoid the stack attack that the message detection
+        // hits. No limit is placed on this because there is no stack risk with
+        // recursion, and because if a limit was hit, there is no other way to
+        // encode the group (the message field can just print as length
+        // delimited, groups don't have an option like that).
+        var groupFieldNumberStack: [Int] = []
+
+        while let tag = try decoder.getTag() {
+            switch tag.wireFormat {
+            case .varint:
+                encoder.emitFieldNumber(number: tag.fieldNumber)
+                var value: UInt64 = 0
+                encoder.startRegularField()
+                try decoder.decodeSingularUInt64Field(value: &value)
+                encoder.putUInt64(value: value)
+                encoder.endRegularField()
+            case .fixed64:
+                encoder.emitFieldNumber(number: tag.fieldNumber)
+                var value: UInt64 = 0
+                encoder.startRegularField()
+                try decoder.decodeSingularFixed64Field(value: &value)
+                encoder.putUInt64Hex(value: value, digits: 16)
+                encoder.endRegularField()
+            case .lengthDelimited:
+                encoder.emitFieldNumber(number: tag.fieldNumber)
+                var bytes = Data()
+                try decoder.decodeSingularBytesField(value: &bytes)
+                var encodeAsBytes = true
+                if bytes.count > 0 && recursionBudget > 0 {
+                    bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> Void in
+                        if let baseAddress = body.baseAddress, body.count > 0 {
+                            do {
+                                // Walk all the fields to test if it looks like a message
+                                var testDecoder = BinaryDecoder(
+                                    forReadingFrom: baseAddress,
+                                    count: body.count,
+                                    parent: decoder
+                                )
+                                while let _ = try testDecoder.nextFieldNumber() {
+                                }
+                                // No error?  Output the message body.
+                                encodeAsBytes = false
+                                var subDecoder = BinaryDecoder(
+                                    forReadingFrom: baseAddress,
+                                    count: bytes.count,
+                                    parent: decoder
+                                )
+                                encoder.startMessageField()
+                                try visitUnknown(
+                                    decoder: &subDecoder,
+                                    recursionBudget: recursionBudget - 1
+                                )
+                                encoder.endMessageField()
+                            } catch {
+                                encodeAsBytes = true
+                            }
+                        }
+                    }
+                }
+                if encodeAsBytes {
+                    encoder.startRegularField()
+                    encoder.putBytesValue(value: bytes)
+                    encoder.endRegularField()
+                }
+            case .startGroup:
+                encoder.emitFieldNumber(number: tag.fieldNumber)
+                encoder.startMessageField()
+                groupFieldNumberStack.append(tag.fieldNumber)
+            case .endGroup:
+                let groupFieldNumber = groupFieldNumberStack.popLast()
+                // Unknown data is scanned and verified by the
+                // binary parser, so this can never fail.
+                assert(tag.fieldNumber == groupFieldNumber)
+                encoder.endMessageField()
+            case .fixed32:
+                encoder.emitFieldNumber(number: tag.fieldNumber)
+                var value: UInt32 = 0
                 encoder.startRegularField()
-                encoder.putBytesValue(value: bytes)
+                try decoder.decodeSingularFixed32Field(value: &value)
+                encoder.putUInt64Hex(value: UInt64(value), digits: 8)
                 encoder.endRegularField()
-              }
-          case .startGroup:
-              encoder.emitFieldNumber(number: tag.fieldNumber)
-              encoder.startMessageField()
-              groupFieldNumberStack.append(tag.fieldNumber)
-          case .endGroup:
-              let groupFieldNumber = groupFieldNumberStack.popLast()
-              // Unknown data is scanned and verified by the
-              // binary parser, so this can never fail.
-              assert(tag.fieldNumber == groupFieldNumber)
-              encoder.endMessageField()
-          case .fixed32:
-              encoder.emitFieldNumber(number: tag.fieldNumber)
-              var value: UInt32 = 0
-              encoder.startRegularField()
-              try decoder.decodeSingularFixed32Field(value: &value)
-              encoder.putUInt64Hex(value: UInt64(value), digits: 8)
-              encoder.endRegularField()
-          }
-      }
-
-    // Unknown data is scanned and verified by the binary parser, so this can
-    // never fail.
-    assert(groupFieldNumberStack.isEmpty)
-  }
-
-  // Visitor.swift defines default versions for other singular field types
-  // that simply widen and dispatch to one of the following.  Since Text format
-  // does not distinguish e.g., Fixed64 vs. UInt64, this is sufficient.
-
-  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putFloatValue(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putDoubleValue(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putInt64(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putUInt64(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putBoolValue(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putStringValue(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putBytesValue(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      encoder.putEnumValue(value: value)
-      encoder.endRegularField()
-  }
-
-  mutating func visitSingularMessageField<M: Message>(value: M,
-                                             fieldNumber: Int) throws {
-      emitFieldName(lookingUp: fieldNumber)
-
-      // Cache old visitor configuration
-      let oldNameMap = self.nameMap
-      let oldNameResolver = self.nameResolver
-      let oldExtensions = self.extensions
-      // Update configuration for new message
-    self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap
-      self.nameResolver = [:]
-    self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
-      // Encode submessage
-      encoder.startMessageField()
-      if let any = value as? Google_Protobuf_Any {
-          any.textTraverse(visitor: &self)
-      } else {
-          try! value.traverse(visitor: &self)
-      }
-      encoder.endMessageField()
-      // Restore configuration before returning
-      self.extensions = oldExtensions
-      self.nameResolver = oldNameResolver
-      self.nameMap = oldNameMap
-  }
-
-  // Emit the full "verbose" form of an Any.  This writes the typeURL
-  // as a field name in `[...]` followed by the fields of the
-  // contained message.
-  internal mutating func visitAnyVerbose(value: any Message, typeURL: String) {
-      encoder.emitExtensionFieldName(name: typeURL)
-      encoder.startMessageField()
-
-      // Cache old visitor configuration
-      let oldNameMap = self.nameMap
-      let oldNameResolver = self.nameResolver
-      let oldExtensions = self.extensions
-      // Update configuration for new message
-    self.nameMap = (type(of: value) as? any _ProtoNameProviding.Type)?._protobuf_nameMap
-      self.nameResolver = [:]
-    self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
-
-      if let any = value as? Google_Protobuf_Any {
-          any.textTraverse(visitor: &self)
-      } else {
-          try! value.traverse(visitor: &self)
-      }
-
-      // Restore configuration before returning
-      self.extensions = oldExtensions
-      self.nameResolver = oldNameResolver
-      self.nameMap = oldNameMap
-
-      encoder.endMessageField()
-  }
-
-  // Write a single special field called "#json".  This
-  // is used for Any objects with undecoded JSON contents.
-  internal mutating func visitAnyJSONBytesField(value: Data) {
-      encoder.indent()
-      encoder.append(staticText: "#json: ")
-      encoder.putBytesValue(value: value)
-      encoder.append(staticText: "\n")
-  }
-
-  // The default implementations in Visitor.swift provide the correct
-  // results, but we get significantly better performance by only doing
-  // the name lookup once for the array, rather than once for each element:
-
-  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putFloatValue(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putDoubleValue(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putInt64(value: Int64(v))
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putInt64(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putUInt64(value: UInt64(v))
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putUInt64(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-  mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putBoolValue(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putStringValue(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putBytesValue(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startRegularField()
-          encoder.putEnumValue(value: v)
-          encoder.endRegularField()
-      }
-  }
-
-  // Messages and groups
-  mutating func visitRepeatedMessageField<M: Message>(value: [M],
-                                             fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      // Look up field name against outer message encoding state
-      let fieldName = formatFieldName(lookingUp: fieldNumber)
-      // Cache old visitor state
-      let oldNameMap = self.nameMap
-      let oldNameResolver = self.nameResolver
-      let oldExtensions = self.extensions
-      // Update encoding state for new message type
-    self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap
-      self.nameResolver = [:]
-    self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
-      // Iterate and encode each message
-      for v in value {
-          encoder.emitFieldName(name: fieldName)
-          encoder.startMessageField()
-          if let any = v as? Google_Protobuf_Any {
-              any.textTraverse(visitor: &self)
-          } else {
-              try! v.traverse(visitor: &self)
-          }
-          encoder.endMessageField()
-      }
-      // Restore state
-      self.extensions = oldExtensions
-      self.nameResolver = oldNameResolver
-      self.nameMap = oldNameMap
-  }
-
-  // Google's C++ implementation of Text format supports two formats
-  // for repeated numeric fields: "short" format writes the list as a
-  // single field with values enclosed in `[...]`, "long" format
-  // writes a separate field name/value for each item.  They provide
-  // an option for callers to select which output version they prefer.
-
-  // Since this distinction mirrors the difference in Protobuf Binary
-  // between "packed" and "non-packed", I've chosen to use the short
-  // format for packed fields and the long version for repeated
-  // fields.  This provides a clear visual distinction between these
-  // fields (including proto3's default use of packed) without
-  // introducing the baggage of a separate option.
-
-  private mutating func iterateAndEncode<T>(
-    packedValue: [T], fieldNumber: Int,
-    encode: (T, inout TextFormatEncoder) -> ()
-  ) throws {
-      assert(!packedValue.isEmpty)
-      emitFieldName(lookingUp: fieldNumber)
-      encoder.startRegularField()
-      var firstItem = true
-      encoder.startArray()
-      for v in packedValue {
-          if !firstItem {
-              encoder.arraySeparator()
-          }
-          encode(v, &encoder)
-          firstItem = false
-      }
-      encoder.endArray()
-      encoder.endRegularField()
-  }
-
-  mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: Float, encoder: inout TextFormatEncoder) in
-      encoder.putFloatValue(value: v)
-    }
-  }
-
-  mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: Double, encoder: inout TextFormatEncoder) in
-      encoder.putDoubleValue(value: v)
-    }
-  }
-
-  mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: Int32, encoder: inout TextFormatEncoder) in
-      encoder.putInt64(value: Int64(v))
-    }
-  }
-
-  mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: Int64, encoder: inout TextFormatEncoder) in
-      encoder.putInt64(value: v)
-    }
-  }
-
-  mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: UInt32, encoder: inout TextFormatEncoder) in
-      encoder.putUInt64(value: UInt64(v))
-    }
-  }
-
-  mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: UInt64, encoder: inout TextFormatEncoder) in
-      encoder.putUInt64(value: v)
-    }
-  }
-
-  mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: Bool, encoder: inout TextFormatEncoder) in
-      encoder.putBoolValue(value: v)
-    }
-  }
-
-  mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
-      (v: E, encoder: inout TextFormatEncoder) in
-      encoder.putEnumValue(value: v)
-    }
-  }
-
-  /// Helper to encapsulate the common structure of iterating over a map
-  /// and encoding the keys and values.
-  private mutating func iterateAndEncode<K, V>(
-    map: Dictionary<K, V>,
-    fieldNumber: Int,
-    isOrderedBefore: (K, K) -> Bool,
-    encode: (inout TextFormatEncodingVisitor, K, V) throws -> ()
-  ) throws {
-      // Cache old visitor configuration
-      let oldNameMap = self.nameMap
-      let oldNameResolver = self.nameResolver
-      let oldExtensions = self.extensions
-
-      for (k,v) in map.sorted(by: { isOrderedBefore( $0.0, $1.0) }) {
-          emitFieldName(lookingUp: fieldNumber)
-          encoder.startMessageField()
-
-          // Update visitor configuration for map
-          self.nameMap = nil
-          self.nameResolver = mapNameResolver
-          self.extensions = nil
-
-          try encode(&self, k, v)
-
-          // Restore configuration before resuming containing message
-          self.extensions = oldExtensions
-          self.nameResolver = oldNameResolver
-          self.nameMap = oldNameMap
-
-          encoder.endMessageField()
-      }
-  }
-
-  mutating func visitMapField<KeyType, ValueType: MapValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: _ProtobufMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-      try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
-          (visitor: inout TextFormatEncodingVisitor, key, value) throws -> () in
-          try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-          try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
-      }
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws where ValueType.RawValue == Int {
-      try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
-          (visitor: inout TextFormatEncodingVisitor, key, value) throws -> () in
-          try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-          try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
-      }
-  }
-
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int
-  ) throws {
-      try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
-          (visitor: inout TextFormatEncodingVisitor, key, value) throws -> () in
-          try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
-          try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
-      }
-  }
+            }
+        }
+
+        // Unknown data is scanned and verified by the binary parser, so this can
+        // never fail.
+        assert(groupFieldNumberStack.isEmpty)
+    }
+
+    // Visitor.swift defines default versions for other singular field types
+    // that simply widen and dispatch to one of the following.  Since Text format
+    // does not distinguish e.g., Fixed64 vs. UInt64, this is sufficient.
+
+    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putFloatValue(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putDoubleValue(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putInt64(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putUInt64(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putBoolValue(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putStringValue(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putBytesValue(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        encoder.putEnumValue(value: value)
+        encoder.endRegularField()
+    }
+
+    mutating func visitSingularMessageField<M: Message>(
+        value: M,
+        fieldNumber: Int
+    ) throws {
+        emitFieldName(lookingUp: fieldNumber)
+
+        // Cache old visitor configuration
+        let oldNameMap = self.nameMap
+        let oldNameResolver = self.nameResolver
+        let oldExtensions = self.extensions
+        // Update configuration for new message
+        self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap
+        self.nameResolver = [:]
+        self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
+        // Encode submessage
+        encoder.startMessageField()
+        if let any = value as? Google_Protobuf_Any {
+            any.textTraverse(visitor: &self)
+        } else {
+            try! value.traverse(visitor: &self)
+        }
+        encoder.endMessageField()
+        // Restore configuration before returning
+        self.extensions = oldExtensions
+        self.nameResolver = oldNameResolver
+        self.nameMap = oldNameMap
+    }
+
+    // Emit the full "verbose" form of an Any.  This writes the typeURL
+    // as a field name in `[...]` followed by the fields of the
+    // contained message.
+    internal mutating func visitAnyVerbose(value: any Message, typeURL: String) {
+        encoder.emitExtensionFieldName(name: typeURL)
+        encoder.startMessageField()
+
+        // Cache old visitor configuration
+        let oldNameMap = self.nameMap
+        let oldNameResolver = self.nameResolver
+        let oldExtensions = self.extensions
+        // Update configuration for new message
+        self.nameMap = (type(of: value) as? any _ProtoNameProviding.Type)?._protobuf_nameMap
+        self.nameResolver = [:]
+        self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
+
+        if let any = value as? Google_Protobuf_Any {
+            any.textTraverse(visitor: &self)
+        } else {
+            try! value.traverse(visitor: &self)
+        }
+
+        // Restore configuration before returning
+        self.extensions = oldExtensions
+        self.nameResolver = oldNameResolver
+        self.nameMap = oldNameMap
+
+        encoder.endMessageField()
+    }
+
+    // Write a single special field called "#json".  This
+    // is used for Any objects with undecoded JSON contents.
+    internal mutating func visitAnyJSONBytesField(value: Data) {
+        encoder.indent()
+        encoder.append(staticText: "#json: ")
+        encoder.putBytesValue(value: value)
+        encoder.append(staticText: "\n")
+    }
+
+    // The default implementations in Visitor.swift provide the correct
+    // results, but we get significantly better performance by only doing
+    // the name lookup once for the array, rather than once for each element:
+
+    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putFloatValue(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putDoubleValue(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putInt64(value: Int64(v))
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putInt64(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putUInt64(value: UInt64(v))
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putUInt64(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putBoolValue(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putStringValue(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putBytesValue(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startRegularField()
+            encoder.putEnumValue(value: v)
+            encoder.endRegularField()
+        }
+    }
+
+    // Messages and groups
+    mutating func visitRepeatedMessageField<M: Message>(
+        value: [M],
+        fieldNumber: Int
+    ) throws {
+        assert(!value.isEmpty)
+        // Look up field name against outer message encoding state
+        let fieldName = formatFieldName(lookingUp: fieldNumber)
+        // Cache old visitor state
+        let oldNameMap = self.nameMap
+        let oldNameResolver = self.nameResolver
+        let oldExtensions = self.extensions
+        // Update encoding state for new message type
+        self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap
+        self.nameResolver = [:]
+        self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues
+        // Iterate and encode each message
+        for v in value {
+            encoder.emitFieldName(name: fieldName)
+            encoder.startMessageField()
+            if let any = v as? Google_Protobuf_Any {
+                any.textTraverse(visitor: &self)
+            } else {
+                try! v.traverse(visitor: &self)
+            }
+            encoder.endMessageField()
+        }
+        // Restore state
+        self.extensions = oldExtensions
+        self.nameResolver = oldNameResolver
+        self.nameMap = oldNameMap
+    }
+
+    // Google's C++ implementation of Text format supports two formats
+    // for repeated numeric fields: "short" format writes the list as a
+    // single field with values enclosed in `[...]`, "long" format
+    // writes a separate field name/value for each item.  They provide
+    // an option for callers to select which output version they prefer.
+
+    // Since this distinction mirrors the difference in Protobuf Binary
+    // between "packed" and "non-packed", I've chosen to use the short
+    // format for packed fields and the long version for repeated
+    // fields.  This provides a clear visual distinction between these
+    // fields (including proto3's default use of packed) without
+    // introducing the baggage of a separate option.
+
+    private mutating func iterateAndEncode<T>(
+        packedValue: [T],
+        fieldNumber: Int,
+        encode: (T, inout TextFormatEncoder) -> Void
+    ) throws {
+        assert(!packedValue.isEmpty)
+        emitFieldName(lookingUp: fieldNumber)
+        encoder.startRegularField()
+        var firstItem = true
+        encoder.startArray()
+        for v in packedValue {
+            if !firstItem {
+                encoder.arraySeparator()
+            }
+            encode(v, &encoder)
+            firstItem = false
+        }
+        encoder.endArray()
+        encoder.endRegularField()
+    }
+
+    mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: Float, encoder: inout TextFormatEncoder) in
+            encoder.putFloatValue(value: v)
+        }
+    }
+
+    mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: Double, encoder: inout TextFormatEncoder) in
+            encoder.putDoubleValue(value: v)
+        }
+    }
+
+    mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: Int32, encoder: inout TextFormatEncoder) in
+            encoder.putInt64(value: Int64(v))
+        }
+    }
+
+    mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: Int64, encoder: inout TextFormatEncoder) in
+            encoder.putInt64(value: v)
+        }
+    }
+
+    mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: UInt32, encoder: inout TextFormatEncoder) in
+            encoder.putUInt64(value: UInt64(v))
+        }
+    }
+
+    mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: UInt64, encoder: inout TextFormatEncoder) in
+            encoder.putUInt64(value: v)
+        }
+    }
+
+    mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: Bool, encoder: inout TextFormatEncoder) in
+            encoder.putBoolValue(value: v)
+        }
+    }
+
+    mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) {
+            (v: E, encoder: inout TextFormatEncoder) in
+            encoder.putEnumValue(value: v)
+        }
+    }
+
+    /// Helper to encapsulate the common structure of iterating over a map
+    /// and encoding the keys and values.
+    private mutating func iterateAndEncode<K, V>(
+        map: [K: V],
+        fieldNumber: Int,
+        isOrderedBefore: (K, K) -> Bool,
+        encode: (inout TextFormatEncodingVisitor, K, V) throws -> Void
+    ) throws {
+        // Cache old visitor configuration
+        let oldNameMap = self.nameMap
+        let oldNameResolver = self.nameResolver
+        let oldExtensions = self.extensions
+
+        for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) {
+            emitFieldName(lookingUp: fieldNumber)
+            encoder.startMessageField()
+
+            // Update visitor configuration for map
+            self.nameMap = nil
+            self.nameResolver = mapNameResolver
+            self.extensions = nil
+
+            try encode(&self, k, v)
+
+            // Restore configuration before resuming containing message
+            self.extensions = oldExtensions
+            self.nameResolver = oldNameResolver
+            self.nameMap = oldNameMap
+
+            encoder.endMessageField()
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
+            (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in
+            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+            try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor)
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where ValueType.RawValue == Int {
+        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
+            (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in
+            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+            try visitor.visitSingularEnumField(value: value, fieldNumber: 2)
+        }
+    }
+
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws {
+        try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) {
+            (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in
+            try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor)
+            try visitor.visitSingularMessageField(value: value, fieldNumber: 2)
+        }
+    }
 }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 406 - 388
Sources/SwiftProtobuf/TextFormatScanner.swift


+ 12 - 12
Sources/SwiftProtobuf/TimeUtils.swift

@@ -17,7 +17,7 @@ let minutesPerHour: Int32 = 60
 let secondsPerDay: Int32 = 86400
 let secondsPerHour: Int32 = 3600
 let secondsPerMinute: Int32 = 60
-let nanosPerSecond: Int32 = 1000000000
+let nanosPerSecond: Int32 = 1_000_000_000
 
 internal func timeOfDayFromSecondsSince1970(seconds: Int64) -> (hh: Int32, mm: Int32, ss: Int32) {
     let secondsSinceMidnight = Int32(mod(seconds, Int64(secondsPerDay)))
@@ -31,7 +31,7 @@ internal func timeOfDayFromSecondsSince1970(seconds: Int64) -> (hh: Int32, mm: I
 internal func julianDayNumberFromSecondsSince1970(seconds: Int64) -> Int64 {
     // January 1, 1970 is Julian Day Number 2440588.
     // See http://aa.usno.navy.mil/faq/docs/JD_Formula.php
-    return div(seconds + 2440588 * Int64(secondsPerDay), Int64(secondsPerDay))
+    div(seconds + 2_440_588 * Int64(secondsPerDay), Int64(secondsPerDay))
 }
 
 internal func gregorianDateFromSecondsSince1970(seconds: Int64) -> (YY: Int32, MM: Int32, DD: Int32) {
@@ -53,13 +53,13 @@ internal func gregorianDateFromSecondsSince1970(seconds: Int64) -> (YY: Int32, M
 }
 
 internal func nanosToString(nanos: Int32) -> String {
-  if nanos == 0 {
-    return ""
-  } else if nanos % 1000000 == 0 {
-    return ".\(threeDigit(abs(nanos) / 1000000))"
-  } else if nanos % 1000 == 0 {
-    return ".\(sixDigit(abs(nanos) / 1000))"
-  } else {
-    return ".\(nineDigit(abs(nanos)))"
-  }
-}
+    if nanos == 0 {
+        return ""
+    } else if nanos % 1_000_000 == 0 {
+        return ".\(threeDigit(abs(nanos) / 1_000_000))"
+    } else if nanos % 1000 == 0 {
+        return ".\(sixDigit(abs(nanos) / 1000))"
+    } else {
+        return ".\(nineDigit(abs(nanos)))"
+    }
+}

+ 13 - 13
Sources/SwiftProtobuf/UnknownStorage.swift

@@ -22,22 +22,22 @@ import Foundation
 /// formats (for example, a field encoded as a varint when a fixed32 integer
 /// was expected).
 public struct UnknownStorage: Equatable, @unchecked Sendable {
-  // Once swift(>=5.9) the '@unchecked' can be removed, it is needed for Data in
-  // linux builds.
+    // Once swift(>=5.9) the '@unchecked' can be removed, it is needed for Data in
+    // linux builds.
 
-  /// The raw protocol buffer binary-encoded bytes that represent the unknown
-  /// fields of a decoded message.
-  public private(set) var data = Data()
+    /// The raw protocol buffer binary-encoded bytes that represent the unknown
+    /// fields of a decoded message.
+    public private(set) var data = Data()
 
-  public init() {}
+    public init() {}
 
-  internal mutating func append(protobufData: Data) {
-    data.append(protobufData)
-  }
+    internal mutating func append(protobufData: Data) {
+        data.append(protobufData)
+    }
 
-  public func traverse<V: Visitor>(visitor: inout V) throws {
-    if !data.isEmpty {
-      try visitor.visitUnknown(bytes: data)
+    public func traverse<V: Visitor>(visitor: inout V) throws {
+        if !data.isEmpty {
+            try visitor.visitUnknown(bytes: data)
+        }
     }
-  }
 }

+ 2 - 3
Sources/SwiftProtobuf/UnsafeRawPointer+Shims.swift

@@ -12,14 +12,13 @@
 ///
 // -----------------------------------------------------------------------------
 
-
 extension UnsafeRawPointer {
     /// A shim subscript for UnsafeRawPointer aiming to maintain code consistency.
     ///
     /// We can remove this shim when we rewrite the code to use buffer pointers.
     internal subscript(_ offset: Int) -> UInt8 {
         get {
-            return self.load(fromByteOffset: offset, as: UInt8.self)
+            self.load(fromByteOffset: offset, as: UInt8.self)
         }
     }
 }
@@ -30,7 +29,7 @@ extension UnsafeMutableRawPointer {
     /// We can remove this shim when we rewrite the code to use buffer pointers.
     internal subscript(_ offset: Int) -> UInt8 {
         get {
-            return self.load(fromByteOffset: offset, as: UInt8.self)
+            self.load(fromByteOffset: offset, as: UInt8.self)
         }
         set {
             self.storeBytes(of: newValue, toByteOffset: offset, as: UInt8.self)

+ 79 - 80
Sources/SwiftProtobuf/Varint.swift

@@ -12,97 +12,96 @@
 ///
 // -----------------------------------------------------------------------------
 
-
 /// Contains helper methods to varint-encode and decode integers.
 internal enum Varint {
 
-  /// Computes the number of bytes that would be needed to store a 32-bit varint.
-  ///
-  /// - Parameter value: The number whose varint size should be calculated.
-  /// - Returns: The size, in bytes, of the 32-bit varint.
-  static func encodedSize(of value: UInt32) -> Int {
-    if (value & (~0 << 7)) == 0 {
-      return 1
-    }
-    if (value & (~0 << 14)) == 0 {
-      return 2
-    }
-    if (value & (~0 << 21)) == 0 {
-      return 3
+    /// Computes the number of bytes that would be needed to store a 32-bit varint.
+    ///
+    /// - Parameter value: The number whose varint size should be calculated.
+    /// - Returns: The size, in bytes, of the 32-bit varint.
+    static func encodedSize(of value: UInt32) -> Int {
+        if (value & (~0 << 7)) == 0 {
+            return 1
+        }
+        if (value & (~0 << 14)) == 0 {
+            return 2
+        }
+        if (value & (~0 << 21)) == 0 {
+            return 3
+        }
+        if (value & (~0 << 28)) == 0 {
+            return 4
+        }
+        return 5
     }
-    if (value & (~0 << 28)) == 0 {
-      return 4
-    }
-    return 5
-  }
 
-  /// Computes the number of bytes that would be needed to store a signed 32-bit varint, if it were
-  /// treated as an unsigned integer with the same bit pattern.
-  ///
-  /// - Parameter value: The number whose varint size should be calculated.
-  /// - Returns: The size, in bytes, of the 32-bit varint.
-  static func encodedSize(of value: Int32) -> Int {
-    if value >= 0 {
-      return encodedSize(of: UInt32(bitPattern: value))
-    } else {
-      // Must sign-extend.
-      return encodedSize(of: Int64(value))
+    /// Computes the number of bytes that would be needed to store a signed 32-bit varint, if it were
+    /// treated as an unsigned integer with the same bit pattern.
+    ///
+    /// - Parameter value: The number whose varint size should be calculated.
+    /// - Returns: The size, in bytes, of the 32-bit varint.
+    static func encodedSize(of value: Int32) -> Int {
+        if value >= 0 {
+            return encodedSize(of: UInt32(bitPattern: value))
+        } else {
+            // Must sign-extend.
+            return encodedSize(of: Int64(value))
+        }
     }
-  }
 
-  /// Computes the number of bytes that would be needed to store a 64-bit varint.
-  ///
-  /// - Parameter value: The number whose varint size should be calculated.
-  /// - Returns: The size, in bytes, of the 64-bit varint.
-  static func encodedSize(of value: Int64) -> Int {
-    // Handle two common special cases up front.
-    if (value & (~0 << 7)) == 0 {
-      return 1
-    }
-    if value < 0 {
-      return 10
-    }
+    /// Computes the number of bytes that would be needed to store a 64-bit varint.
+    ///
+    /// - Parameter value: The number whose varint size should be calculated.
+    /// - Returns: The size, in bytes, of the 64-bit varint.
+    static func encodedSize(of value: Int64) -> Int {
+        // Handle two common special cases up front.
+        if (value & (~0 << 7)) == 0 {
+            return 1
+        }
+        if value < 0 {
+            return 10
+        }
 
-    // Divide and conquer the remaining eight cases.
-    var value = value
-    var n = 2
+        // Divide and conquer the remaining eight cases.
+        var value = value
+        var n = 2
 
-    if (value & (~0 << 35)) != 0 {
-      n += 4
-      value >>= 28
-    }
-    if (value & (~0 << 21)) != 0 {
-      n += 2
-      value >>= 14
+        if (value & (~0 << 35)) != 0 {
+            n += 4
+            value >>= 28
+        }
+        if (value & (~0 << 21)) != 0 {
+            n += 2
+            value >>= 14
+        }
+        if (value & (~0 << 14)) != 0 {
+            n += 1
+        }
+        return n
     }
-    if (value & (~0 << 14)) != 0 {
-      n += 1
-    }
-    return n
-  }
 
-  /// Computes the number of bytes that would be needed to store an unsigned 64-bit varint, if it
-  /// were treated as a signed integer with the same bit pattern.
-  ///
-  /// - Parameter value: The number whose varint size should be calculated.
-  /// - Returns: The size, in bytes, of the 64-bit varint.
-  static func encodedSize(of value: UInt64) -> Int {
-    return encodedSize(of: Int64(bitPattern: value))
-  }
+    /// Computes the number of bytes that would be needed to store an unsigned 64-bit varint, if it
+    /// were treated as a signed integer with the same bit pattern.
+    ///
+    /// - Parameter value: The number whose varint size should be calculated.
+    /// - Returns: The size, in bytes, of the 64-bit varint.
+    static func encodedSize(of value: UInt64) -> Int {
+        encodedSize(of: Int64(bitPattern: value))
+    }
 
-  /// Counts the number of distinct varints in a packed byte buffer.
-  static func countVarintsInBuffer(start: UnsafeRawPointer, count: Int) -> Int {
-    // We don't need to decode all the varints to count how many there
-    // are.  Just observe that every varint has exactly one byte with
-    // value < 128. So we just count those...
-    var n = 0
-    var ints = 0
-    while n < count {
-      if start.load(fromByteOffset: n, as: UInt8.self) < 128 {
-        ints += 1
-      }
-      n += 1
+    /// Counts the number of distinct varints in a packed byte buffer.
+    static func countVarintsInBuffer(start: UnsafeRawPointer, count: Int) -> Int {
+        // We don't need to decode all the varints to count how many there
+        // are.  Just observe that every varint has exactly one byte with
+        // value < 128. So we just count those...
+        var n = 0
+        var ints = 0
+        while n < count {
+            if start.load(fromByteOffset: n, as: UInt8.self) < 128 {
+                ints += 1
+            }
+            n += 1
+        }
+        return ints
     }
-    return ints
-  }
 }

+ 8 - 8
Sources/SwiftProtobuf/Version.swift

@@ -16,13 +16,13 @@ import Foundation
 
 // Expose version information about the library.
 public struct Version {
-  /// Major version.
-  public static let major = 1
-  /// Minor version.
-  public static let minor = 28
-  /// Revision number.
-  public static let revision = 1
+    /// Major version.
+    public static let major = 1
+    /// Minor version.
+    public static let minor = 28
+    /// Revision number.
+    public static let revision = 1
 
-  /// String form of the version number.
-  public static let versionString = "\(major).\(minor).\(revision)"
+    /// String form of the version number.
+    public static let versionString = "\(major).\(minor).\(revision)"
 }

+ 696 - 685
Sources/SwiftProtobuf/Visitor.swift

@@ -32,694 +32,705 @@ import Foundation
 /// Protobuf Binary, Protobuf Text, JSON, and the Hash encoder.
 public protocol Visitor {
 
-  /// Called for each non-repeated float field
-  ///
-  /// A default implementation is provided that just widens the value
-  /// and calls `visitSingularDoubleField`
-  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws
-
-  /// Called for each non-repeated double field
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws
-
-  /// Called for each non-repeated int32 field
-  ///
-  /// A default implementation is provided that just widens the value
-  /// and calls `visitSingularInt64Field`
-  mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws
-
-  /// Called for each non-repeated int64 field
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws
-
-  /// Called for each non-repeated uint32 field
-  ///
-  /// A default implementation is provided that just widens the value
-  /// and calls `visitSingularUInt64Field`
-  mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws
-
-  /// Called for each non-repeated uint64 field
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws
-
-  /// Called for each non-repeated sint32 field
-  ///
-  /// A default implementation is provided that just forwards to
-  /// `visitSingularInt32Field`
-  mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws
-
-  /// Called for each non-repeated sint64 field
-  ///
-  /// A default implementation is provided that just forwards to
-  /// `visitSingularInt64Field`
-  mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws
-
-  /// Called for each non-repeated fixed32 field
-  ///
-  /// A default implementation is provided that just forwards to
-  /// `visitSingularUInt32Field`
-  mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws
-
-  /// Called for each non-repeated fixed64 field
-  ///
-  /// A default implementation is provided that just forwards to
-  /// `visitSingularUInt64Field`
-  mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws
-
-  /// Called for each non-repeated sfixed32 field
-  ///
-  /// A default implementation is provided that just forwards to
-  /// `visitSingularInt32Field`
-  mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws
-
-  /// Called for each non-repeated sfixed64 field
-  ///
-  /// A default implementation is provided that just forwards to
-  /// `visitSingularInt64Field`
-  mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws
-
-  /// Called for each non-repeated bool field
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws
-
-  /// Called for each non-repeated string field
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws
-
-  /// Called for each non-repeated bytes field
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws
-
-  /// Called for each non-repeated enum field
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws
-
-  /// Called for each non-repeated nested message field.
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws
-
-  /// Called for each non-repeated proto2 group field.
-  ///
-  /// A default implementation is provided that simply forwards to
-  /// `visitSingularMessageField`. Implementors who need to handle groups
-  /// differently than nested messages can override this and provide distinct
-  /// implementations.
-  mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws
-
-  // Called for each non-packed repeated float field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularFloatField` once for each item in the array.
-  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated double field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularDoubleField` once for each item in the array.
-  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated int32 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularInt32Field` once for each item in the array.
-  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated int64 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularInt64Field` once for each item in the array.
-  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated uint32 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularUInt32Field` once for each item in the array.
-  mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated uint64 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularUInt64Field` once for each item in the array.
-  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated sint32 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularSInt32Field` once for each item in the array.
-  mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated sint64 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularSInt64Field` once for each item in the array.
-  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated fixed32 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularFixed32Field` once for each item in the array.
-  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated fixed64 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularFixed64Field` once for each item in the array.
-  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated sfixed32 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularSFixed32Field` once for each item in the array.
-  mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated sfixed64 field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularSFixed64Field` once for each item in the array.
-  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated bool field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularBoolField` once for each item in the array.
-  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated string field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularStringField` once for each item in the array.
-  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws
-
-  // Called for each non-packed repeated bytes field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularBytesField` once for each item in the array.
-  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws
-
-  /// Called for each repeated, unpacked enum field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularEnumField` once for each item in the array.
-  mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws
-
-  /// Called for each repeated nested message field. The method is called once
-  /// with the complete array of values for the field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularMessageField` once for each item in the array.
-  mutating func visitRepeatedMessageField<M: Message>(value: [M],
-                                                      fieldNumber: Int) throws
-
-  /// Called for each repeated proto2 group field.
-  ///
-  /// A default implementation is provided that simply calls
-  /// `visitSingularGroupField` once for each item in the array.
-  mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws
-
-  // Called for each packed, repeated float field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws
-
-  // Called for each packed, repeated double field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws
-
-  // Called for each packed, repeated int32 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws
-
-  // Called for each packed, repeated int64 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws
-
-  // Called for each packed, repeated uint32 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws
-
-  // Called for each packed, repeated uint64 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws
-
-  // Called for each packed, repeated sint32 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws
-
-  // Called for each packed, repeated sint64 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws
-
-  // Called for each packed, repeated fixed32 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws
-
-  // Called for each packed, repeated fixed64 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws
-
-  // Called for each packed, repeated sfixed32 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws
-
-  // Called for each packed, repeated sfixed64 field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws
-
-  // Called for each packed, repeated bool field.
-  ///
-  /// This is called once with the complete array of values for
-  /// the field.
-  ///
-  /// There is a default implementation that forwards to the non-packed
-  /// function.
-  mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws
-
-  /// Called for each repeated, packed enum field.
-  /// The method is called once with the complete array of values for
-  /// the field.
-  ///
-  /// A default implementation is provided that simply forwards to
-  /// `visitRepeatedEnumField`. Implementors who need to handle packed fields
-  /// differently than unpacked fields can override this and provide distinct
-  /// implementations.
-  mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws
-
-  /// Called for each map field with primitive values. The method is
-  /// called once with the complete dictionary of keys/values for the
-  /// field.
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitMapField<KeyType, ValueType: MapValueType>(
-    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
-    value: _ProtobufMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int) throws
-
-  /// Called for each map field with enum values. The method is called
-  /// once with the complete dictionary of keys/values for the field.
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
-    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int) throws where ValueType.RawValue == Int
-
-  /// Called for each map field with message values. The method is
-  /// called once with the complete dictionary of keys/values for the
-  /// field.
-  ///
-  /// There is no default implementation.  This must be implemented.
-  mutating func visitMapField<KeyType, ValueType>(
-    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
-    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
-    fieldNumber: Int) throws
-
-  /// Called for each extension range.
-  mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws
-
-  /// Called for each extension range.
-  mutating func visitExtensionFieldsAsMessageSet(
-    fields: ExtensionFieldValueSet,
-    start: Int,
-    end: Int) throws
-
-  /// Called with the raw bytes that represent any unknown fields.
-  mutating func visitUnknown(bytes: Data) throws
+    /// Called for each non-repeated float field
+    ///
+    /// A default implementation is provided that just widens the value
+    /// and calls `visitSingularDoubleField`
+    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws
+
+    /// Called for each non-repeated double field
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws
+
+    /// Called for each non-repeated int32 field
+    ///
+    /// A default implementation is provided that just widens the value
+    /// and calls `visitSingularInt64Field`
+    mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws
+
+    /// Called for each non-repeated int64 field
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws
+
+    /// Called for each non-repeated uint32 field
+    ///
+    /// A default implementation is provided that just widens the value
+    /// and calls `visitSingularUInt64Field`
+    mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws
+
+    /// Called for each non-repeated uint64 field
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws
+
+    /// Called for each non-repeated sint32 field
+    ///
+    /// A default implementation is provided that just forwards to
+    /// `visitSingularInt32Field`
+    mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws
+
+    /// Called for each non-repeated sint64 field
+    ///
+    /// A default implementation is provided that just forwards to
+    /// `visitSingularInt64Field`
+    mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws
+
+    /// Called for each non-repeated fixed32 field
+    ///
+    /// A default implementation is provided that just forwards to
+    /// `visitSingularUInt32Field`
+    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws
+
+    /// Called for each non-repeated fixed64 field
+    ///
+    /// A default implementation is provided that just forwards to
+    /// `visitSingularUInt64Field`
+    mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws
+
+    /// Called for each non-repeated sfixed32 field
+    ///
+    /// A default implementation is provided that just forwards to
+    /// `visitSingularInt32Field`
+    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws
+
+    /// Called for each non-repeated sfixed64 field
+    ///
+    /// A default implementation is provided that just forwards to
+    /// `visitSingularInt64Field`
+    mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws
+
+    /// Called for each non-repeated bool field
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws
+
+    /// Called for each non-repeated string field
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws
+
+    /// Called for each non-repeated bytes field
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws
+
+    /// Called for each non-repeated enum field
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws
+
+    /// Called for each non-repeated nested message field.
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws
+
+    /// Called for each non-repeated proto2 group field.
+    ///
+    /// A default implementation is provided that simply forwards to
+    /// `visitSingularMessageField`. Implementors who need to handle groups
+    /// differently than nested messages can override this and provide distinct
+    /// implementations.
+    mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws
+
+    // Called for each non-packed repeated float field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularFloatField` once for each item in the array.
+    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated double field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularDoubleField` once for each item in the array.
+    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated int32 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularInt32Field` once for each item in the array.
+    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated int64 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularInt64Field` once for each item in the array.
+    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated uint32 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularUInt32Field` once for each item in the array.
+    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated uint64 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularUInt64Field` once for each item in the array.
+    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated sint32 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularSInt32Field` once for each item in the array.
+    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated sint64 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularSInt64Field` once for each item in the array.
+    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated fixed32 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularFixed32Field` once for each item in the array.
+    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated fixed64 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularFixed64Field` once for each item in the array.
+    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated sfixed32 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularSFixed32Field` once for each item in the array.
+    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated sfixed64 field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularSFixed64Field` once for each item in the array.
+    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated bool field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularBoolField` once for each item in the array.
+    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated string field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularStringField` once for each item in the array.
+    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws
+
+    // Called for each non-packed repeated bytes field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularBytesField` once for each item in the array.
+    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws
+
+    /// Called for each repeated, unpacked enum field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularEnumField` once for each item in the array.
+    mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws
+
+    /// Called for each repeated nested message field. The method is called once
+    /// with the complete array of values for the field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularMessageField` once for each item in the array.
+    mutating func visitRepeatedMessageField<M: Message>(
+        value: [M],
+        fieldNumber: Int
+    ) throws
+
+    /// Called for each repeated proto2 group field.
+    ///
+    /// A default implementation is provided that simply calls
+    /// `visitSingularGroupField` once for each item in the array.
+    mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws
+
+    // Called for each packed, repeated float field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws
+
+    // Called for each packed, repeated double field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws
+
+    // Called for each packed, repeated int32 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws
+
+    // Called for each packed, repeated int64 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws
+
+    // Called for each packed, repeated uint32 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws
+
+    // Called for each packed, repeated uint64 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws
+
+    // Called for each packed, repeated sint32 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws
+
+    // Called for each packed, repeated sint64 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws
+
+    // Called for each packed, repeated fixed32 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws
+
+    // Called for each packed, repeated fixed64 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws
+
+    // Called for each packed, repeated sfixed32 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws
+
+    // Called for each packed, repeated sfixed64 field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws
+
+    // Called for each packed, repeated bool field.
+    ///
+    /// This is called once with the complete array of values for
+    /// the field.
+    ///
+    /// There is a default implementation that forwards to the non-packed
+    /// function.
+    mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws
+
+    /// Called for each repeated, packed enum field.
+    /// The method is called once with the complete array of values for
+    /// the field.
+    ///
+    /// A default implementation is provided that simply forwards to
+    /// `visitRepeatedEnumField`. Implementors who need to handle packed fields
+    /// differently than unpacked fields can override this and provide distinct
+    /// implementations.
+    mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws
+
+    /// Called for each map field with primitive values. The method is
+    /// called once with the complete dictionary of keys/values for the
+    /// field.
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitMapField<KeyType, ValueType: MapValueType>(
+        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+        value: _ProtobufMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws
+
+    /// Called for each map field with enum values. The method is called
+    /// once with the complete dictionary of keys/values for the field.
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws where ValueType.RawValue == Int
+
+    /// Called for each map field with message values. The method is
+    /// called once with the complete dictionary of keys/values for the
+    /// field.
+    ///
+    /// There is no default implementation.  This must be implemented.
+    mutating func visitMapField<KeyType, ValueType>(
+        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+        fieldNumber: Int
+    ) throws
+
+    /// Called for each extension range.
+    mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws
+
+    /// Called for each extension range.
+    mutating func visitExtensionFieldsAsMessageSet(
+        fields: ExtensionFieldValueSet,
+        start: Int,
+        end: Int
+    ) throws
+
+    /// Called with the raw bytes that represent any unknown fields.
+    mutating func visitUnknown(bytes: Data) throws
 }
 
 /// Forwarding default implementations of some visitor methods, for convenience.
 extension Visitor {
 
-  // Default definitions of numeric serializations.
-  //
-  // The 32-bit versions widen and delegate to 64-bit versions.
-  // The specialized integer codings delegate to standard Int/UInt.
-  //
-  // These "just work" for Hash and Text formats.  Most of these work
-  // for JSON (32-bit integers are overridden to suppress quoting),
-  // and a few even work for Protobuf Binary (thanks to varint coding
-  // which erases the size difference between 32-bit and 64-bit ints).
-
-  public mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
-    try visitSingularDoubleField(value: Double(value), fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
-    try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
-    try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
-    try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
-    try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
-    try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
-    try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
-    try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-  public mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
-    try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  // Default definitions of repeated serializations that just iterate and
-  // invoke the singular encoding.  These "just work" for Protobuf Binary (encoder
-  // and size visitor), Protobuf Text, and Hash visitors.  JSON format stores
-  // repeated values differently from singular, so overrides these.
-
-  public mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularFloatField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularDoubleField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularInt32Field(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularInt64Field(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularUInt32Field(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularUInt64Field(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      for v in value {
-          try visitSingularSInt32Field(value: v, fieldNumber: fieldNumber)
-      }
-  }
-
-  public mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      for v in value {
-          try visitSingularSInt64Field(value: v, fieldNumber: fieldNumber)
-      }
-  }
-
-  public mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      for v in value {
-          try visitSingularFixed32Field(value: v, fieldNumber: fieldNumber)
-      }
-  }
-
-  public mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      for v in value {
-          try visitSingularFixed64Field(value: v, fieldNumber: fieldNumber)
-      }
-  }
-
-  public mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      for v in value {
-          try visitSingularSFixed32Field(value: v, fieldNumber: fieldNumber)
-      }
-  }
-
-  public mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-      assert(!value.isEmpty)
-      for v in value {
-          try visitSingularSFixed64Field(value: v, fieldNumber: fieldNumber)
-      }
-  }
-
-  public mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularBoolField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularStringField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularBytesField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-        try visitSingularEnumField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularMessageField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  public mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    for v in value {
-      try visitSingularGroupField(value: v, fieldNumber: fieldNumber)
-    }
-  }
-
-  // Default definitions of packed serialization just defer to the
-  // repeated implementation.  This works for Hash and JSON visitors
-  // (which do not distinguish packed vs. non-packed) but are
-  // overridden by Protobuf Binary and Text.
-
-  public mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedFloatField(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedDoubleField(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedBoolField(value: value, fieldNumber: fieldNumber)
-  }
-
-  public mutating func visitPackedEnumField<E: Enum>(value: [E],
-                                            fieldNumber: Int) throws {
-    assert(!value.isEmpty)
-    try visitRepeatedEnumField(value: value, fieldNumber: fieldNumber)
-  }
-
-  // Default handling for Groups is to treat them just like messages.
-  // This works for Text and Hash, but is overridden by Protobuf Binary
-  // format (which has a different encoding for groups) and JSON
-  // (which explicitly ignores all groups).
-
-  public mutating func visitSingularGroupField<G: Message>(value: G,
-                                                  fieldNumber: Int) throws {
-    try visitSingularMessageField(value: value, fieldNumber: fieldNumber)
-  }
-
-  // Default handling of Extensions as a MessageSet to handing them just
-  // as plain extensions. Formats that what custom behavior can override
-  // it.
-
-  public mutating func visitExtensionFieldsAsMessageSet(
-    fields: ExtensionFieldValueSet,
-    start: Int,
-    end: Int) throws {
-    try visitExtensionFields(fields: fields, start: start, end: end)
-  }
-
-  // Default handling for Extensions is to forward the traverse to
-  // the ExtensionFieldValueSet. Formats that don't care about extensions
-  // can override to avoid it.
-
-  /// Called for each extension range.
-  public mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws {
-    try fields.traverse(visitor: &self, start: start, end: end)
-  }
+    // Default definitions of numeric serializations.
+    //
+    // The 32-bit versions widen and delegate to 64-bit versions.
+    // The specialized integer codings delegate to standard Int/UInt.
+    //
+    // These "just work" for Hash and Text formats.  Most of these work
+    // for JSON (32-bit integers are overridden to suppress quoting),
+    // and a few even work for Protobuf Binary (thanks to varint coding
+    // which erases the size difference between 32-bit and 64-bit ints).
+
+    public mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+        try visitSingularDoubleField(value: Double(value), fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
+        try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
+        try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+        try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
+        try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+        try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+    public mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
+        try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    // Default definitions of repeated serializations that just iterate and
+    // invoke the singular encoding.  These "just work" for Protobuf Binary (encoder
+    // and size visitor), Protobuf Text, and Hash visitors.  JSON format stores
+    // repeated values differently from singular, so overrides these.
+
+    public mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularFloatField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularDoubleField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularInt32Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularInt64Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularUInt32Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularUInt64Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularSInt32Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularSInt64Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularFixed32Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularFixed64Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularSFixed32Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularSFixed64Field(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularBoolField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularStringField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularBytesField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularEnumField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularMessageField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    public mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        for v in value {
+            try visitSingularGroupField(value: v, fieldNumber: fieldNumber)
+        }
+    }
+
+    // Default definitions of packed serialization just defer to the
+    // repeated implementation.  This works for Hash and JSON visitors
+    // (which do not distinguish packed vs. non-packed) but are
+    // overridden by Protobuf Binary and Text.
+
+    public mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedFloatField(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedDoubleField(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitPackedInt32Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitPackedInt64Field(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedBoolField(value: value, fieldNumber: fieldNumber)
+    }
+
+    public mutating func visitPackedEnumField<E: Enum>(
+        value: [E],
+        fieldNumber: Int
+    ) throws {
+        assert(!value.isEmpty)
+        try visitRepeatedEnumField(value: value, fieldNumber: fieldNumber)
+    }
+
+    // Default handling for Groups is to treat them just like messages.
+    // This works for Text and Hash, but is overridden by Protobuf Binary
+    // format (which has a different encoding for groups) and JSON
+    // (which explicitly ignores all groups).
+
+    public mutating func visitSingularGroupField<G: Message>(
+        value: G,
+        fieldNumber: Int
+    ) throws {
+        try visitSingularMessageField(value: value, fieldNumber: fieldNumber)
+    }
+
+    // Default handling of Extensions as a MessageSet to handing them just
+    // as plain extensions. Formats that what custom behavior can override
+    // it.
+
+    public mutating func visitExtensionFieldsAsMessageSet(
+        fields: ExtensionFieldValueSet,
+        start: Int,
+        end: Int
+    ) throws {
+        try visitExtensionFields(fields: fields, start: start, end: end)
+    }
+
+    // Default handling for Extensions is to forward the traverse to
+    // the ExtensionFieldValueSet. Formats that don't care about extensions
+    // can override to avoid it.
+
+    /// Called for each extension range.
+    public mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws {
+        try fields.traverse(visitor: &self, start: start, end: end)
+    }
 }

+ 45 - 47
Sources/SwiftProtobuf/WireFormat.swift

@@ -14,57 +14,55 @@
 
 /// Denotes the wire format by which a value is encoded in binary form.
 internal enum WireFormat: UInt8 {
-  case varint = 0
-  case fixed64 = 1
-  case lengthDelimited = 2
-  case startGroup = 3
-  case endGroup = 4
-  case fixed32 = 5
+    case varint = 0
+    case fixed64 = 1
+    case lengthDelimited = 2
+    case startGroup = 3
+    case endGroup = 4
+    case fixed32 = 5
 }
 
 extension WireFormat {
-  /// Information about the "MessageSet" format. Used when a Message has
-  /// the message_set_wire_format option enabled.
-  ///
-  /// Writing in MessageSet form means instead of writing the Extesions
-  /// normally as a simple fields, each gets written wrapped in a group:
-  ///   repeated group Item = 1 {
-  ///     required int32 type_id = 2;
-  ///     required bytes message = 3;
-  ///   }
-  ///  Where the field number is the type_id, and the message is serilaized
-  ///  into the bytes.
-  ///
-  /// The handling of unknown fields is ill defined. In proto1, they were
-  /// dropped. In the C++ for proto2, since it stores them in the unknowns
-  /// storage, if preserves any that are length delimited data (since that's
-  /// how the message also goes out). While the C++ is parsing, where the
-  /// unknowns fall in the flow of the group, sorta decides what happens.
-  /// Since it is ill defined, currently SwiftProtobuf will reflect out
-  /// anything set in the unknownStorage.  During parsing, unknowns on the
-  /// message are preserved, but unknowns within the group are dropped (like
-  /// map items).  Any extension in the MessageSet that isn't in the Regisry
-  /// being used at parse time will remain in a group and go into the
-  /// Messages's unknown fields (this way it reflects back out correctly).
-  internal enum MessageSet {
+    /// Information about the "MessageSet" format. Used when a Message has
+    /// the message_set_wire_format option enabled.
+    ///
+    /// Writing in MessageSet form means instead of writing the Extesions
+    /// normally as a simple fields, each gets written wrapped in a group:
+    ///   repeated group Item = 1 {
+    ///     required int32 type_id = 2;
+    ///     required bytes message = 3;
+    ///   }
+    ///  Where the field number is the type_id, and the message is serilaized
+    ///  into the bytes.
+    ///
+    /// The handling of unknown fields is ill defined. In proto1, they were
+    /// dropped. In the C++ for proto2, since it stores them in the unknowns
+    /// storage, if preserves any that are length delimited data (since that's
+    /// how the message also goes out). While the C++ is parsing, where the
+    /// unknowns fall in the flow of the group, sorta decides what happens.
+    /// Since it is ill defined, currently SwiftProtobuf will reflect out
+    /// anything set in the unknownStorage.  During parsing, unknowns on the
+    /// message are preserved, but unknowns within the group are dropped (like
+    /// map items).  Any extension in the MessageSet that isn't in the Regisry
+    /// being used at parse time will remain in a group and go into the
+    /// Messages's unknown fields (this way it reflects back out correctly).
+    internal enum MessageSet {
 
-    enum FieldNumbers {
-      static let item = 1;
-      static let typeId = 2;
-      static let message = 3;
-    }
+        enum FieldNumbers {
+            static let item = 1
+            static let typeId = 2
+            static let message = 3
+        }
 
-    enum Tags {
-      static let itemStart = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .startGroup)
-      static let itemEnd = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .endGroup)
-      static let typeId = FieldTag(fieldNumber: FieldNumbers.typeId, wireFormat: .varint)
-      static let message = FieldTag(fieldNumber: FieldNumbers.message, wireFormat: .lengthDelimited)
-    }
+        enum Tags {
+            static let itemStart = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .startGroup)
+            static let itemEnd = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .endGroup)
+            static let typeId = FieldTag(fieldNumber: FieldNumbers.typeId, wireFormat: .varint)
+            static let message = FieldTag(fieldNumber: FieldNumbers.message, wireFormat: .lengthDelimited)
+        }
 
-    // The size of all the tags needed to write out an Extension in MessageSet format.
-    static let itemTagsEncodedSize =
-      Tags.itemStart.encodedSize + Tags.itemEnd.encodedSize +
-        Tags.typeId.encodedSize +
-        Tags.message.encodedSize
-  }
+        // The size of all the tags needed to write out an Extension in MessageSet format.
+        static let itemTagsEncodedSize =
+            Tags.itemStart.encodedSize + Tags.itemEnd.encodedSize + Tags.typeId.encodedSize + Tags.message.encodedSize
+    }
 }

+ 4 - 5
Sources/SwiftProtobuf/ZigZag.swift

@@ -12,7 +12,6 @@
 ///
 // -----------------------------------------------------------------------------
 
-
 /// Contains helper methods to ZigZag encode and decode signed integers.
 internal enum ZigZag {
 
@@ -25,7 +24,7 @@ internal enum ZigZag {
     /// - Parameter value: A signed 32-bit integer.
     /// - Returns: An unsigned 32-bit integer representing the ZigZag-encoded value.
     static func encoded(_ value: Int32) -> UInt32 {
-        return UInt32(bitPattern: (value << 1) ^ (value >> 31))
+        UInt32(bitPattern: (value << 1) ^ (value >> 31))
     }
 
     /// Return a 64-bit ZigZag-encoded value.
@@ -37,7 +36,7 @@ internal enum ZigZag {
     /// - Parameter value: A signed 64-bit integer.
     /// - Returns: An unsigned 64-bit integer representing the ZigZag-encoded value.
     static func encoded(_ value: Int64) -> UInt64 {
-        return UInt64(bitPattern: (value << 1) ^ (value >> 63))
+        UInt64(bitPattern: (value << 1) ^ (value >> 63))
     }
 
     /// Return a 32-bit ZigZag-decoded value.
@@ -49,7 +48,7 @@ internal enum ZigZag {
     /// - Parameter value: An unsigned 32-bit ZagZag-encoded integer.
     /// - Returns: The signed 32-bit decoded value.
     static func decoded(_ value: UInt32) -> Int32 {
-        return Int32(value >> 1) ^ -Int32(value & 1)
+        Int32(value >> 1) ^ -Int32(value & 1)
     }
 
     /// Return a 64-bit ZigZag-decoded value.
@@ -61,6 +60,6 @@ internal enum ZigZag {
     /// - Parameter value: An unsigned 64-bit ZigZag-encoded integer.
     /// - Returns: The signed 64-bit decoded value.
     static func decoded(_ value: UInt64) -> Int64 {
-        return Int64(value >> 1) ^ -Int64(value & 1)
+        Int64(value >> 1) ^ -Int64(value & 1)
     }
 }

+ 269 - 254
Sources/SwiftProtobufPluginLibrary/CodeGenerator.swift

@@ -13,187 +13,191 @@
 // -----------------------------------------------------------------------------
 
 import Foundation
-
 import SwiftProtobuf
 
 /// A protocol that generator should conform to then get easy support for
 /// being a protocol buffer compiler pluign.
 public protocol CodeGenerator {
-  init()
-
-  /// Generates code for the given proto files.
-  ///
-  /// - Parameters:
-  ///   - parameter: The parameter (or paramenters) passed for the generator.
-  ///       This is for parameters specific to this generator,
-  ///       `parse(parameter:)` (below) can be used to split back out
-  ///       multiple parameters into the combined for the protocol buffer
-  ///       compiler uses.
-  ///   - protoCompilerContext: Context information about the protocol buffer
-  ///       compiler being used.
-  ///   - generatorOutputs: A object that can be used to send back the
-  ///       generated outputs.
-  ///
-  /// - Throws: Can throw any `Error` to fail generate. `String(describing:)`
-  ///       will be called on the error to provide the error string reported
-  ///       to the user attempting to generate sources.
-  func generate(
-    files: [FileDescriptor],
-    parameter: any CodeGeneratorParameter,
-    protoCompilerContext: any ProtoCompilerContext,
-    generatorOutputs: any GeneratorOutputs) throws
-
-  /// The list of features this CodeGenerator support to be reported back to
-  /// the protocol buffer compiler.
-  var supportedFeatures: [Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] { get }
-
-  /// The Protobuf Edition range that this generator can handle. Attempting
-  /// to generate for an Edition outside this range will cause protoc to
-  /// error.
-  var supportedEditionRange: ClosedRange<Google_Protobuf_Edition> { get }
-
-  /// A list of extensions that define Custom Options
-  /// (https://protobuf.dev/programming-guides/proto2/#customoptions) for this generator so
-  /// they will be exposed on the `Descriptor` options.
-  var customOptionExtensions: [any AnyMessageExtension] { get }
-
-  /// If provided, the argument parsing will support `--version` and report
-  /// this value.
-  var version: String? { get }
-
-  /// If provided and `printHelp` isn't provide, this value will be including in
-  /// default output for the `--help` output.
-  var projectURL: String? { get }
-
-  /// If provided and `printHelp` isn't provide, this value will be including in
-  /// default output for the `--help` output.
-  var copyrightLine: String? { get }
-
-  /// Will be called for `-h` or `--help`, should `print()` out whatever is
-  /// desired; there is a default implementation that uses the above info
-  /// when provided.
-  func printHelp()
+    init()
+
+    /// Generates code for the given proto files.
+    ///
+    /// - Parameters:
+    ///   - parameter: The parameter (or paramenters) passed for the generator.
+    ///       This is for parameters specific to this generator,
+    ///       `parse(parameter:)` (below) can be used to split back out
+    ///       multiple parameters into the combined for the protocol buffer
+    ///       compiler uses.
+    ///   - protoCompilerContext: Context information about the protocol buffer
+    ///       compiler being used.
+    ///   - generatorOutputs: A object that can be used to send back the
+    ///       generated outputs.
+    ///
+    /// - Throws: Can throw any `Error` to fail generate. `String(describing:)`
+    ///       will be called on the error to provide the error string reported
+    ///       to the user attempting to generate sources.
+    func generate(
+        files: [FileDescriptor],
+        parameter: any CodeGeneratorParameter,
+        protoCompilerContext: any ProtoCompilerContext,
+        generatorOutputs: any GeneratorOutputs
+    ) throws
+
+    /// The list of features this CodeGenerator support to be reported back to
+    /// the protocol buffer compiler.
+    var supportedFeatures: [Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] { get }
+
+    /// The Protobuf Edition range that this generator can handle. Attempting
+    /// to generate for an Edition outside this range will cause protoc to
+    /// error.
+    var supportedEditionRange: ClosedRange<Google_Protobuf_Edition> { get }
+
+    /// A list of extensions that define Custom Options
+    /// (https://protobuf.dev/programming-guides/proto2/#customoptions) for this generator so
+    /// they will be exposed on the `Descriptor` options.
+    var customOptionExtensions: [any AnyMessageExtension] { get }
+
+    /// If provided, the argument parsing will support `--version` and report
+    /// this value.
+    var version: String? { get }
+
+    /// If provided and `printHelp` isn't provide, this value will be including in
+    /// default output for the `--help` output.
+    var projectURL: String? { get }
+
+    /// If provided and `printHelp` isn't provide, this value will be including in
+    /// default output for the `--help` output.
+    var copyrightLine: String? { get }
+
+    /// Will be called for `-h` or `--help`, should `print()` out whatever is
+    /// desired; there is a default implementation that uses the above info
+    /// when provided.
+    func printHelp()
 }
 
 extension CommandLine {
-  /// Get the command-line arguments passed to this process in a non mutable
-  /// form. Idea from https://github.com/swiftlang/swift/issues/66213
-  ///
-  /// - Returns: An array of command-line arguments.
-  fileprivate static let safeArguments: [String] =
-    UnsafeBufferPointer(start: unsafeArgv, count: Int(argc)).compactMap {
-      String(validatingUTF8: $0!)
-    }
+    /// Get the command-line arguments passed to this process in a non mutable
+    /// form. Idea from https://github.com/swiftlang/swift/issues/66213
+    ///
+    /// - Returns: An array of command-line arguments.
+    fileprivate static let safeArguments: [String] =
+        UnsafeBufferPointer(start: unsafeArgv, count: Int(argc)).compactMap {
+            String(validatingUTF8: $0!)
+        }
 }
 
 extension CodeGenerator {
-  var programName: String {
-    guard let name = CommandLine.safeArguments.first?.split(separator: "/").last else {
-      return "<plugin>"
-    }
-    return String(name)
-  }
-
-  /// Runs as a protocol buffer compiler plugin based on the given arguments
-  /// or falls back to `CommandLine.arguments`.
-  public func main(_ args: [String]?) {
-    let args = args ?? Array(CommandLine.safeArguments.dropFirst())
-
-    for arg in args {
-      if arg == "--version", let version = version {
-        print("\(programName) \(version)")
-        return
-      }
-      if arg == "-h" || arg == "--help" {
-        printHelp()
-        return
-      }
-      // Could look at bringing back the support for recorded requests, but
-      // haven't needed it in a long time.
-      var stderr = StandardErrorOutputStream()
-      print("Unknown argument: \(arg)", to: &stderr)
-      return
+    var programName: String {
+        guard let name = CommandLine.safeArguments.first?.split(separator: "/").last else {
+            return "<plugin>"
+        }
+        return String(name)
     }
 
-    var extensionMap = SimpleExtensionMap()
-    if !customOptionExtensions.isEmpty {
-      for e in customOptionExtensions {
-        // Don't include Google_Protobuf_FeatureSet, that will be handing via custom features.
-        precondition(e.messageType == Google_Protobuf_EnumOptions.self ||
-                     e.messageType == Google_Protobuf_EnumValueOptions.self ||
-                     e.messageType == Google_Protobuf_ExtensionRangeOptions.self ||
-                     e.messageType == Google_Protobuf_FieldOptions.self ||
-                     e.messageType == Google_Protobuf_FileOptions.self ||
-                     e.messageType == Google_Protobuf_MessageOptions.self ||
-                     e.messageType == Google_Protobuf_MethodOptions.self ||
-                     e.messageType == Google_Protobuf_OneofOptions.self ||
-                     e.messageType == Google_Protobuf_ServiceOptions.self,
-                     "CodeGenerator `customOptionExtensions` must only extend the descriptor.proto 'Options' messages \(e.messageType).")
-      }
-      extensionMap.insert(contentsOf: customOptionExtensions)
+    /// Runs as a protocol buffer compiler plugin based on the given arguments
+    /// or falls back to `CommandLine.arguments`.
+    public func main(_ args: [String]?) {
+        let args = args ?? Array(CommandLine.safeArguments.dropFirst())
+
+        for arg in args {
+            if arg == "--version", let version = version {
+                print("\(programName) \(version)")
+                return
+            }
+            if arg == "-h" || arg == "--help" {
+                printHelp()
+                return
+            }
+            // Could look at bringing back the support for recorded requests, but
+            // haven't needed it in a long time.
+            var stderr = StandardErrorOutputStream()
+            print("Unknown argument: \(arg)", to: &stderr)
+            return
+        }
+
+        var extensionMap = SimpleExtensionMap()
+        if !customOptionExtensions.isEmpty {
+            for e in customOptionExtensions {
+                // Don't include Google_Protobuf_FeatureSet, that will be handing via custom features.
+                precondition(
+                    e.messageType == Google_Protobuf_EnumOptions.self
+                        || e.messageType == Google_Protobuf_EnumValueOptions.self
+                        || e.messageType == Google_Protobuf_ExtensionRangeOptions.self
+                        || e.messageType == Google_Protobuf_FieldOptions.self
+                        || e.messageType == Google_Protobuf_FileOptions.self
+                        || e.messageType == Google_Protobuf_MessageOptions.self
+                        || e.messageType == Google_Protobuf_MethodOptions.self
+                        || e.messageType == Google_Protobuf_OneofOptions.self
+                        || e.messageType == Google_Protobuf_ServiceOptions.self,
+                    "CodeGenerator `customOptionExtensions` must only extend the descriptor.proto 'Options' messages \(e.messageType)."
+                )
+            }
+            extensionMap.insert(contentsOf: customOptionExtensions)
+        }
+
+        let response: Google_Protobuf_Compiler_CodeGeneratorResponse
+        do {
+            let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(
+                serializedBytes: FileHandle.standardInput.readDataToEndOfFile(),
+                extensions: extensionMap
+            )
+            response = generateCode(request: request, generator: self)
+        } catch let e {
+            response = Google_Protobuf_Compiler_CodeGeneratorResponse(
+                error: "Received an unparsable request from the compiler: \(e)"
+            )
+        }
+
+        let serializedResponse: Data
+        do {
+            serializedResponse = try response.serializedBytes()
+        } catch let e {
+            var stderr = StandardErrorOutputStream()
+            print("\(programName): Failure while serializing response: \(e)", to: &stderr)
+            return
+        }
+        FileHandle.standardOutput.write(serializedResponse)
     }
 
-    let response: Google_Protobuf_Compiler_CodeGeneratorResponse
-    do {
-      let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(
-        serializedBytes: FileHandle.standardInput.readDataToEndOfFile(),
-        extensions: extensionMap
-      )
-      response = generateCode(request: request, generator: self)
-    } catch let e {
-      response = Google_Protobuf_Compiler_CodeGeneratorResponse(
-        error: "Received an unparsable request from the compiler: \(e)")
+    /// Runs as a protocol buffer compiler plugin; reading the generation request
+    /// off stdin and sending the response on stdout.
+    ///
+    /// Instead of calling this, just add `@main` to your `CodeGenerator`.
+    public static func main() {
+        let generator = Self()
+        generator.main(nil)
     }
-
-    let serializedResponse: Data
-    do {
-      serializedResponse = try response.serializedBytes()
-    } catch let e {
-      var stderr = StandardErrorOutputStream()
-      print("\(programName): Failure while serializing response: \(e)", to: &stderr)
-      return
-    }
-    FileHandle.standardOutput.write(serializedResponse)
-  }
-
-  /// Runs as a protocol buffer compiler plugin; reading the generation request
-  /// off stdin and sending the response on stdout.
-  ///
-  /// Instead of calling this, just add `@main` to your `CodeGenerator`.
-  public static func main() {
-    let generator = Self()
-    generator.main(nil)
-  }
 }
 
 // Provide default implementation for things so `CodeGenerator`s only have to
 // provide them if they wish too.
 extension CodeGenerator {
-  public var supportedEditionRange: ClosedRange<Google_Protobuf_Edition> {
-    // Default impl of unknown so generator don't have to provide this until
-    // they support editions.
-    return Google_Protobuf_Edition.unknown...Google_Protobuf_Edition.unknown
-  }
-  public var customOptionExtensions: [any AnyMessageExtension] { return [] }
-  public var version: String? { return nil }
-  public var projectURL: String? { return nil }
-  public var copyrightLine: String? { return nil }
-
-  public func printHelp() {
-    print("\(programName): A plugin for protoc and should not normally be run directly.")
-    if let copyright = copyrightLine {
-      print("\(copyright)")
+    public var supportedEditionRange: ClosedRange<Google_Protobuf_Edition> {
+        // Default impl of unknown so generator don't have to provide this until
+        // they support editions.
+        Google_Protobuf_Edition.unknown...Google_Protobuf_Edition.unknown
     }
-    if let projectURL = projectURL {
-      print(
-        """
-
-        For more information on the usage of this plugin, please see:
-          \(projectURL)
-
-        """)
+    public var customOptionExtensions: [any AnyMessageExtension] { [] }
+    public var version: String? { nil }
+    public var projectURL: String? { nil }
+    public var copyrightLine: String? { nil }
+
+    public func printHelp() {
+        print("\(programName): A plugin for protoc and should not normally be run directly.")
+        if let copyright = copyrightLine {
+            print("\(copyright)")
+        }
+        if let projectURL = projectURL {
+            print(
+                """
+
+                For more information on the usage of this plugin, please see:
+                  \(projectURL)
+
+                """
+            )
+        }
     }
-  }
 }
 
 /// Uses the given `Google_Protobuf_Compiler_CodeGeneratorRequest` and
@@ -209,62 +213,71 @@ extension CodeGenerator {
 /// - Returns a filled out response with the success or failure of the
 ///    generation.
 public func generateCode(
-  request: Google_Protobuf_Compiler_CodeGeneratorRequest,
-  generator: any CodeGenerator
+    request: Google_Protobuf_Compiler_CodeGeneratorRequest,
+    generator: any CodeGenerator
 ) -> Google_Protobuf_Compiler_CodeGeneratorResponse {
-  // TODO: This will need update to language specific features.
+    // TODO: This will need update to language specific features.
+
+    let descriptorSet = DescriptorSet(protos: request.protoFile)
+
+    var files = [FileDescriptor]()
+    for name in request.fileToGenerate {
+        guard let fileDescriptor = descriptorSet.fileDescriptor(named: name) else {
+            return Google_Protobuf_Compiler_CodeGeneratorResponse(
+                error:
+                    "protoc asked plugin to generate a file but did not provide a descriptor for the file: \(name)"
+            )
+        }
+        files.append(fileDescriptor)
+    }
+
+    let context = InternalProtoCompilerContext(request: request)
+    let outputs = InternalGeneratorOutputs()
+    let parameter = InternalCodeGeneratorParameter(request.parameter)
 
-  let descriptorSet = DescriptorSet(protos: request.protoFile)
+    do {
+        try generator.generate(
+            files: files,
+            parameter: parameter,
+            protoCompilerContext: context,
+            generatorOutputs: outputs
+        )
+    } catch let e {
+        return Google_Protobuf_Compiler_CodeGeneratorResponse(error: String(describing: e))
+    }
 
-  var files = [FileDescriptor]()
-  for name in request.fileToGenerate {
-    guard let fileDescriptor = descriptorSet.fileDescriptor(named: name) else {
-      return Google_Protobuf_Compiler_CodeGeneratorResponse(
-        error:
-          "protoc asked plugin to generate a file but did not provide a descriptor for the file: \(name)"
-      )
+    var response = Google_Protobuf_Compiler_CodeGeneratorResponse()
+    response.file = outputs.files
+
+    // TODO: Could supportedFeatures be completely handled within library?
+    // - The only "hard" part around hiding the proto3 optional support is making
+    //   sure the oneof index related bits aren't leaked from FieldDescriptors.
+    //   Otherwise the oneof related apis could likely take over the "realOneof"
+    //   jobs and just never vend the synthetic information.
+    // - The editions support bit likely could be computed based on the values
+    //   `supportedEditionRange` having been overridden.
+    let supportedFeatures = generator.supportedFeatures
+    response.supportedFeatures = supportedFeatures.reduce(0) { $0 | UInt64($1.rawValue) }
+
+    if supportedFeatures.contains(.supportsEditions) {
+        let supportedEditions = generator.supportedEditionRange
+        precondition(
+            supportedEditions.upperBound != .unknown,
+            "For a CodeGenerator to support Editions, it must override `supportedEditionRange`"
+        )
+        precondition(
+            DescriptorSet.bundledEditionsSupport.contains(supportedEditions.lowerBound),
+            "A CodeGenerator can't claim to support an Edition before what the library supports: \(supportedEditions.lowerBound) vs \(DescriptorSet.bundledEditionsSupport)"
+        )
+        precondition(
+            DescriptorSet.bundledEditionsSupport.contains(supportedEditions.upperBound),
+            "A CodeGenerator can't claim to support an Edition after what the library supports: \(supportedEditions.upperBound) vs \(DescriptorSet.bundledEditionsSupport)"
+        )
+        response.minimumEdition = Int32(supportedEditions.lowerBound.rawValue)
+        response.maximumEdition = Int32(supportedEditions.upperBound.rawValue)
     }
-    files.append(fileDescriptor)
-  }
-
-  let context = InternalProtoCompilerContext(request: request)
-  let outputs = InternalGeneratorOutputs()
-  let parameter = InternalCodeGeneratorParameter(request.parameter)
-
-  do {
-    try generator.generate(
-      files: files, parameter: parameter, protoCompilerContext: context,
-      generatorOutputs: outputs)
-  } catch let e {
-    return Google_Protobuf_Compiler_CodeGeneratorResponse(error: String(describing: e))
-  }
-
-  var response = Google_Protobuf_Compiler_CodeGeneratorResponse()
-  response.file = outputs.files
-
-  // TODO: Could supportedFeatures be completely handled within library?
-  // - The only "hard" part around hiding the proto3 optional support is making
-  //   sure the oneof index related bits aren't leaked from FieldDescriptors.
-  //   Otherwise the oneof related apis could likely take over the "realOneof"
-  //   jobs and just never vend the synthetic information.
-  // - The editions support bit likely could be computed based on the values
-  //   `supportedEditionRange` having been overridden.
-  let supportedFeatures = generator.supportedFeatures
-  response.supportedFeatures = supportedFeatures.reduce(0) { $0 | UInt64($1.rawValue) }
-
-  if supportedFeatures.contains(.supportsEditions) {
-    let supportedEditions = generator.supportedEditionRange
-    precondition(supportedEditions.upperBound != .unknown,
-                 "For a CodeGenerator to support Editions, it must override `supportedEditionRange`")
-    precondition(DescriptorSet.bundledEditionsSupport.contains(supportedEditions.lowerBound),
-                 "A CodeGenerator can't claim to support an Edition before what the library supports: \(supportedEditions.lowerBound) vs \(DescriptorSet.bundledEditionsSupport)")
-    precondition(DescriptorSet.bundledEditionsSupport.contains(supportedEditions.upperBound),
-                 "A CodeGenerator can't claim to support an Edition after what the library supports: \(supportedEditions.upperBound) vs \(DescriptorSet.bundledEditionsSupport)")
-    response.minimumEdition = Int32(supportedEditions.lowerBound.rawValue)
-    response.maximumEdition = Int32(supportedEditions.upperBound.rawValue)
-  }
-
-  return response
+
+    return response
 }
 
 // MARK: Internal supporting types
@@ -272,67 +285,69 @@ public func generateCode(
 /// Internal implementation of `CodeGeneratorParameter` for
 /// `generateCode(request:generator:)`
 struct InternalCodeGeneratorParameter: CodeGeneratorParameter {
-  let parameter: String
-
-  init(_ parameter: String) {
-    self.parameter = parameter
-  }
+    let parameter: String
 
-  var parsedPairs: [(key: String, value: String)] {
-    guard !parameter.isEmpty else {
-      return []
+    init(_ parameter: String) {
+        self.parameter = parameter
     }
-    let parts = parameter.components(separatedBy: ",")
-    return parts.map { s -> (key: String, value: String) in
-      guard let index = s.range(of: "=")?.lowerBound else {
-        // Key only, no value ("baz" in example).
-        return (trimWhitespace(s), "")
-      }
-      return (
-        key: trimWhitespace(s[..<index]),
-        value: trimWhitespace(s[s.index(after: index)...])
-      )
+
+    var parsedPairs: [(key: String, value: String)] {
+        guard !parameter.isEmpty else {
+            return []
+        }
+        let parts = parameter.components(separatedBy: ",")
+        return parts.map { s -> (key: String, value: String) in
+            guard let index = s.range(of: "=")?.lowerBound else {
+                // Key only, no value ("baz" in example).
+                return (trimWhitespace(s), "")
+            }
+            return (
+                key: trimWhitespace(s[..<index]),
+                value: trimWhitespace(s[s.index(after: index)...])
+            )
+        }
     }
-  }
 }
 
 /// Internal implementation of `ProtoCompilerContext` for
 /// `generateCode(request:generator:)`
 private struct InternalProtoCompilerContext: ProtoCompilerContext {
-  let version: Google_Protobuf_Compiler_Version?
+    let version: Google_Protobuf_Compiler_Version?
 
-  init(request: Google_Protobuf_Compiler_CodeGeneratorRequest) {
-    self.version = request.hasCompilerVersion ? request.compilerVersion : nil
-  }
+    init(request: Google_Protobuf_Compiler_CodeGeneratorRequest) {
+        self.version = request.hasCompilerVersion ? request.compilerVersion : nil
+    }
 }
 
 /// Internal implementation of `GeneratorOutputs` for
 /// `generateCode(request:generator:)`
 private final class InternalGeneratorOutputs: GeneratorOutputs {
 
-  enum OutputError: Error, CustomStringConvertible {
-    /// Attempt to add two files with the same name.
-    case duplicateName(String)
+    enum OutputError: Error, CustomStringConvertible {
+        /// Attempt to add two files with the same name.
+        case duplicateName(String)
 
-    var description: String {
-      switch self {
-      case .duplicateName(let name):
-        return "Generator tried to generate two files named \(name)."
-      }
+        var description: String {
+            switch self {
+            case .duplicateName(let name):
+                return "Generator tried to generate two files named \(name)."
+            }
+        }
     }
-  }
-
-  var files: [Google_Protobuf_Compiler_CodeGeneratorResponse.File] = []
-  private var fileNames: Set<String> = []
 
-  func add(fileName: String, contents: String) throws {
-    guard !fileNames.contains(fileName) else {
-      throw OutputError.duplicateName(fileName)
+    var files: [Google_Protobuf_Compiler_CodeGeneratorResponse.File] = []
+    private var fileNames: Set<String> = []
+
+    func add(fileName: String, contents: String) throws {
+        guard !fileNames.contains(fileName) else {
+            throw OutputError.duplicateName(fileName)
+        }
+        fileNames.insert(fileName)
+        files.append(
+            Google_Protobuf_Compiler_CodeGeneratorResponse.File(
+                name: fileName,
+                content: contents
+            )
+        )
     }
-    fileNames.insert(fileName)
-    files.append(
-      Google_Protobuf_Compiler_CodeGeneratorResponse.File(
-        name: fileName,
-        content: contents))
-  }
 }

+ 16 - 16
Sources/SwiftProtobufPluginLibrary/CodeGeneratorParameter.swift

@@ -20,21 +20,21 @@ import Foundation
 /// parameters via the `--[LANG]_out` or `--[LANG]_opt` command line flags.
 /// The compiler will relay those through as a _parameter_ string.
 public protocol CodeGeneratorParameter {
-  /// The raw value from the compiler as a single string, if multiple values
-  /// were passed, they are joined into a single string. See `parsedPairs` as
-  /// that is likely a better option for consuming the parameters.
-  var parameter: String { get }
+    /// The raw value from the compiler as a single string, if multiple values
+    /// were passed, they are joined into a single string. See `parsedPairs` as
+    /// that is likely a better option for consuming the parameters.
+    var parameter: String { get }
 
-  /// The protocol buffer compiler will combine multiple `--[LANG]_opt`
-  /// directives into a "single" parameter by joining them with commas. This
-  /// vends the parameter split back back out into the individual arguments:
-  /// i.e.,
-  ///   "foo=bar,baz,mumble=blah"
-  /// becomes:
-  ///   [
-  ///     (key: "foo", value: "bar"),
-  ///     (key: "baz", value: ""),
-  ///     (key: "mumble", value: "blah")
-  ///   ]
-  var parsedPairs: [(key: String, value: String)] { get }
+    /// The protocol buffer compiler will combine multiple `--[LANG]_opt`
+    /// directives into a "single" parameter by joining them with commas. This
+    /// vends the parameter split back back out into the individual arguments:
+    /// i.e.,
+    ///   "foo=bar,baz,mumble=blah"
+    /// becomes:
+    ///   [
+    ///     (key: "foo", value: "bar"),
+    ///     (key: "baz", value: ""),
+    ///     (key: "mumble", value: "blah")
+    ///   ]
+    var parsedPairs: [(key: String, value: String)] { get }
 }

+ 202 - 200
Sources/SwiftProtobufPluginLibrary/CodePrinter.swift

@@ -17,210 +17,212 @@
 /// `outdent`.
 public struct CodePrinter {
 
-  /// Reserve an initial buffer of 64KB scalars to eliminate some reallocations
-  /// in smaller files.
-  private static let initialBufferSize = 65536
-
-  private static let kNewline : String.UnicodeScalarView.Element = "\n"
-
-  /// The string content that was printed.
-  public var content: String {
-    return String(contentScalars)
-  }
-
-  /// See if anything was printed.
-  public var isEmpty: Bool { return contentScalars.isEmpty }
-
-  /// The Unicode scalar buffer used to build up the printed contents.
-  private var contentScalars = String.UnicodeScalarView()
-
-  /// The `UnicodeScalarView` representing a single indentation step.
-  private let singleIndent: String.UnicodeScalarView
-
-  /// The current indentation level (a collection of spaces).
-  private var indentation = String.UnicodeScalarView()
-
-  /// Keeps track of whether the printer is currently sitting at the beginning
-  /// of a line.
-  private var atLineStart = true
-
-  /// Keeps track of if a newline should be added after each string to the
-  /// print apis.
-  private let newlines: Bool
-
-  public init(indent: String.UnicodeScalarView = "  ".unicodeScalars) {
-    contentScalars.reserveCapacity(CodePrinter.initialBufferSize)
-    singleIndent = indent
-    newlines = false
-  }
-
-  /// Initialize the printer for use.
-  ///
-  /// - Parameters:
-  ///   - indent: A string (usually spaces) to use for the indentation amount.
-  ///   - newlines: A boolean indicating if every `print` and `printIndented`
-  ///       should automatically add newlines to the end of the strings.
-  public init(
-    indent: String.UnicodeScalarView = "  ".unicodeScalars,
-    addNewlines newlines: Bool
-  ) {
-    contentScalars.reserveCapacity(CodePrinter.initialBufferSize)
-    singleIndent = indent
-    self.newlines = newlines
-  }
-
-  /// Initialize a new printer using the existing state from another printer.
-  ///
-  /// This can be useful to use with generation subtasks, so see if they
-  /// actually generate something (via `isEmpty`) to then optionally add it
-  /// back into the parent with whatever surounding content.
-  ///
-  /// This is most useful to then use `append` to add the new content.
-  ///
-  /// - Parameter parent: The other printer to copy the configuration/state
-  ///     from.
-  public init(_ parent: CodePrinter) {
-    self.init(parent, addNewlines: parent.newlines)
-  }
-
-  /// Initialize a new printer using the existing state from another printer
-  /// but with support to control the behavior of `addNewlines`.
-  ///
-  /// This can be useful to use with generation subtasks, so see if they
-  /// actually generate something (via `isEmpty`) to then optionally add it
-  /// back into the parent with whatever surounding content.
-  ///
-  /// This is most useful to then use `append` to add the new content.
-  ///
-  /// - Parameters:
-  ///   - parent: The other printer to copy the configuration/state
-  ///       from.
-  ///   - newlines: A boolean indicating if every `print` and `printIndented`
-  ///       should automatically add newlines to the end of the strings.
-  public init(_ parent: CodePrinter, addNewlines newlines: Bool) {
-    self.init(indent: parent.singleIndent, addNewlines: newlines)
-    indentation = parent.indentation
-  }
-
-  /// Writes the given strings to the printer, adding a newline after each
-  /// string.
-  ///
-  /// Newlines within the strings are honored and indentention is applied.
-  ///
-  /// The `addNewlines` value from initializing the printer controls if
-  /// newlines are appended after each string.
-  ///
-  /// If called with no strings, a blank line is added to the printer
-  /// (even is `addNewlines` was false at initialization of the printer.
-  ///
-  /// - Parameter text: A variable-length list of strings to be printed.
-  public mutating func print(_ text: String...) {
-    if text.isEmpty {
-      contentScalars.append(CodePrinter.kNewline)
-      atLineStart = true
-    } else {
-      for t in text {
-        printInternal(t.unicodeScalars, addNewline: newlines)
-      }
+    /// Reserve an initial buffer of 64KB scalars to eliminate some reallocations
+    /// in smaller files.
+    private static let initialBufferSize = 65536
+
+    private static let kNewline: String.UnicodeScalarView.Element = "\n"
+
+    /// The string content that was printed.
+    public var content: String {
+        String(contentScalars)
+    }
+
+    /// See if anything was printed.
+    public var isEmpty: Bool { contentScalars.isEmpty }
+
+    /// The Unicode scalar buffer used to build up the printed contents.
+    private var contentScalars = String.UnicodeScalarView()
+
+    /// The `UnicodeScalarView` representing a single indentation step.
+    private let singleIndent: String.UnicodeScalarView
+
+    /// The current indentation level (a collection of spaces).
+    private var indentation = String.UnicodeScalarView()
+
+    /// Keeps track of whether the printer is currently sitting at the beginning
+    /// of a line.
+    private var atLineStart = true
+
+    /// Keeps track of if a newline should be added after each string to the
+    /// print apis.
+    private let newlines: Bool
+
+    public init(indent: String.UnicodeScalarView = "  ".unicodeScalars) {
+        contentScalars.reserveCapacity(CodePrinter.initialBufferSize)
+        singleIndent = indent
+        newlines = false
     }
-  }
-
-  /// Writes the given strings to the printer, optionally adding a newline
-  /// after each string. If called with no strings, a blank line is added to
-  /// the printer.
-  ///
-  /// Newlines within the strings are honored and indentention is applied.
-  ///
-  /// - Parameters
-  ///   - text: A variable-length list of strings to be printed.
-  ///   - newlines: Boolean to control adding newlines after each string. This
-  ///       is an explicit override of the `addNewlines` value using to
-  ///       initialize this `CodePrinter`.
-  public mutating func print(_ text: String..., newlines: Bool) {
-    if text.isEmpty {
-      assert(newlines,
-             "Disabling newlines with no strings doesn't make sense.")
-      contentScalars.append(CodePrinter.kNewline)
-      atLineStart = true
-    } else {
-      for t in text {
-        printInternal(t.unicodeScalars, addNewline: newlines)
-      }
+
+    /// Initialize the printer for use.
+    ///
+    /// - Parameters:
+    ///   - indent: A string (usually spaces) to use for the indentation amount.
+    ///   - newlines: A boolean indicating if every `print` and `printIndented`
+    ///       should automatically add newlines to the end of the strings.
+    public init(
+        indent: String.UnicodeScalarView = "  ".unicodeScalars,
+        addNewlines newlines: Bool
+    ) {
+        contentScalars.reserveCapacity(CodePrinter.initialBufferSize)
+        singleIndent = indent
+        self.newlines = newlines
     }
-  }
-
-  /// Indents, writes the given strings to the printer, and then outdents.
-  ///
-  /// Newlines within the strings are honored and indentention is applied.
-  ///
-  /// The `addNewlines` value from initializing the printer controls if
-  /// newlines are appended after each string.
-  ///
-  /// - Parameter text: A variable-length list of strings to be printed.
-  public mutating func printIndented(_ text: String...) {
-    indent()
-    for t in text {
-      printInternal(t.unicodeScalars, addNewline: newlines)
+
+    /// Initialize a new printer using the existing state from another printer.
+    ///
+    /// This can be useful to use with generation subtasks, so see if they
+    /// actually generate something (via `isEmpty`) to then optionally add it
+    /// back into the parent with whatever surounding content.
+    ///
+    /// This is most useful to then use `append` to add the new content.
+    ///
+    /// - Parameter parent: The other printer to copy the configuration/state
+    ///     from.
+    public init(_ parent: CodePrinter) {
+        self.init(parent, addNewlines: parent.newlines)
     }
-    outdent()
-  }
-
-  private mutating func printInternal(
-    _ scalars: String.UnicodeScalarView,
-    addNewline: Bool
-  ) {
-    for scalar in scalars {
-      // Indent at the start of a new line, unless it's a blank line.
-      if atLineStart && scalar != CodePrinter.kNewline {
-        contentScalars.append(contentsOf: indentation)
-      }
-      contentScalars.append(scalar)
-      atLineStart = (scalar == CodePrinter.kNewline)
+
+    /// Initialize a new printer using the existing state from another printer
+    /// but with support to control the behavior of `addNewlines`.
+    ///
+    /// This can be useful to use with generation subtasks, so see if they
+    /// actually generate something (via `isEmpty`) to then optionally add it
+    /// back into the parent with whatever surounding content.
+    ///
+    /// This is most useful to then use `append` to add the new content.
+    ///
+    /// - Parameters:
+    ///   - parent: The other printer to copy the configuration/state
+    ///       from.
+    ///   - newlines: A boolean indicating if every `print` and `printIndented`
+    ///       should automatically add newlines to the end of the strings.
+    public init(_ parent: CodePrinter, addNewlines newlines: Bool) {
+        self.init(indent: parent.singleIndent, addNewlines: newlines)
+        indentation = parent.indentation
     }
-    if addNewline {
-      contentScalars.append(CodePrinter.kNewline)
-      atLineStart = true
+
+    /// Writes the given strings to the printer, adding a newline after each
+    /// string.
+    ///
+    /// Newlines within the strings are honored and indentention is applied.
+    ///
+    /// The `addNewlines` value from initializing the printer controls if
+    /// newlines are appended after each string.
+    ///
+    /// If called with no strings, a blank line is added to the printer
+    /// (even is `addNewlines` was false at initialization of the printer.
+    ///
+    /// - Parameter text: A variable-length list of strings to be printed.
+    public mutating func print(_ text: String...) {
+        if text.isEmpty {
+            contentScalars.append(CodePrinter.kNewline)
+            atLineStart = true
+        } else {
+            for t in text {
+                printInternal(t.unicodeScalars, addNewline: newlines)
+            }
+        }
     }
-  }
-
-  /// Appended the content of another `CodePrinter`to this one.
-  ///
-  /// - Parameters:
-  ///   - printer: The other `CodePrinter` to copy from.
-  ///   - indenting: Boolean, if the text being appended should be reindented
-  ///       to the current state of this printer. If the `printer` was
-  ///       initialized off of this printer, there isn't a need to reindent.
-  public mutating func append(_ printer: CodePrinter, indenting: Bool = false) {
-    if indenting {
-      printInternal(printer.contentScalars, addNewline: false)
-    } else {
-      contentScalars.append(contentsOf: printer.contentScalars)
-      atLineStart = printer.atLineStart
+
+    /// Writes the given strings to the printer, optionally adding a newline
+    /// after each string. If called with no strings, a blank line is added to
+    /// the printer.
+    ///
+    /// Newlines within the strings are honored and indentention is applied.
+    ///
+    /// - Parameters
+    ///   - text: A variable-length list of strings to be printed.
+    ///   - newlines: Boolean to control adding newlines after each string. This
+    ///       is an explicit override of the `addNewlines` value using to
+    ///       initialize this `CodePrinter`.
+    public mutating func print(_ text: String..., newlines: Bool) {
+        if text.isEmpty {
+            assert(
+                newlines,
+                "Disabling newlines with no strings doesn't make sense."
+            )
+            contentScalars.append(CodePrinter.kNewline)
+            atLineStart = true
+        } else {
+            for t in text {
+                printInternal(t.unicodeScalars, addNewline: newlines)
+            }
+        }
+    }
+
+    /// Indents, writes the given strings to the printer, and then outdents.
+    ///
+    /// Newlines within the strings are honored and indentention is applied.
+    ///
+    /// The `addNewlines` value from initializing the printer controls if
+    /// newlines are appended after each string.
+    ///
+    /// - Parameter text: A variable-length list of strings to be printed.
+    public mutating func printIndented(_ text: String...) {
+        indent()
+        for t in text {
+            printInternal(t.unicodeScalars, addNewline: newlines)
+        }
+        outdent()
+    }
+
+    private mutating func printInternal(
+        _ scalars: String.UnicodeScalarView,
+        addNewline: Bool
+    ) {
+        for scalar in scalars {
+            // Indent at the start of a new line, unless it's a blank line.
+            if atLineStart && scalar != CodePrinter.kNewline {
+                contentScalars.append(contentsOf: indentation)
+            }
+            contentScalars.append(scalar)
+            atLineStart = (scalar == CodePrinter.kNewline)
+        }
+        if addNewline {
+            contentScalars.append(CodePrinter.kNewline)
+            atLineStart = true
+        }
+    }
+
+    /// Appended the content of another `CodePrinter`to this one.
+    ///
+    /// - Parameters:
+    ///   - printer: The other `CodePrinter` to copy from.
+    ///   - indenting: Boolean, if the text being appended should be reindented
+    ///       to the current state of this printer. If the `printer` was
+    ///       initialized off of this printer, there isn't a need to reindent.
+    public mutating func append(_ printer: CodePrinter, indenting: Bool = false) {
+        if indenting {
+            printInternal(printer.contentScalars, addNewline: false)
+        } else {
+            contentScalars.append(contentsOf: printer.contentScalars)
+            atLineStart = printer.atLineStart
+        }
+    }
+
+    /// Increases the printer's indentation level.
+    public mutating func indent() {
+        indentation.append(contentsOf: singleIndent)
+    }
+
+    /// Decreases the printer's indentation level.
+    ///
+    /// - Precondition: The printer must not have an indentation level.
+    public mutating func outdent() {
+        let indentCount = singleIndent.count
+        precondition(indentation.count >= indentCount, "Cannot outdent past the left margin")
+        indentation.removeLast(indentCount)
+    }
+
+    /// Indents, calls `body` to do other work relaying along the printer, and
+    /// the outdents after wards.
+    ///
+    /// - Parameter body: A closure that is invoked after the indent is
+    ///     increasted.
+    public mutating func withIndentation(body: (_ p: inout CodePrinter) -> Void) {
+        indent()
+        body(&self)
+        outdent()
     }
-  }
-
-  /// Increases the printer's indentation level.
-  public mutating func indent() {
-    indentation.append(contentsOf: singleIndent)
-  }
-
-  /// Decreases the printer's indentation level.
-  ///
-  /// - Precondition: The printer must not have an indentation level.
-  public mutating func outdent() {
-    let indentCount = singleIndent.count
-    precondition(indentation.count >= indentCount, "Cannot outdent past the left margin")
-    indentation.removeLast(indentCount)
-  }
-
-  /// Indents, calls `body` to do other work relaying along the printer, and
-  /// the outdents after wards.
-  ///
-  /// - Parameter body: A closure that is invoked after the indent is
-  ///     increasted.
-  public mutating func withIndentation(body: (_ p: inout CodePrinter) -> Void) {
-    indent()
-    body(&self)
-    outdent()
-  }
 }

+ 136 - 134
Sources/SwiftProtobufPluginLibrary/Descriptor+Extensions.swift

@@ -12,174 +12,176 @@ import Foundation
 import SwiftProtobuf
 
 extension FileDescriptor: ProvidesSourceCodeLocation {
-  public var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? {
-    // google/protobuf's descriptor.cc says it should be an empty path.
-    return sourceCodeInfoLocation(path: IndexPath())
-  }
+    public var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? {
+        // google/protobuf's descriptor.cc says it should be an empty path.
+        sourceCodeInfoLocation(path: IndexPath())
+    }
 }
 
 extension Descriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, TypeOrFileProvidesDeprecationComment {
-  public func getLocationPath(path: inout IndexPath) {
-    if let containingType = containingType {
-      containingType.getLocationPath(path: &path)
-      path.append(Google_Protobuf_DescriptorProto.FieldNumbers.nestedType)
-    } else {
-      path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.messageType)
+    public func getLocationPath(path: inout IndexPath) {
+        if let containingType = containingType {
+            containingType.getLocationPath(path: &path)
+            path.append(Google_Protobuf_DescriptorProto.FieldNumbers.nestedType)
+        } else {
+            path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.messageType)
+        }
+        path.append(index)
     }
-    path.append(index)
-  }
 
-  public var typeName: String { "message" }
-  public var isDeprecated: Bool { options.deprecated }
+    public var typeName: String { "message" }
+    public var isDeprecated: Bool { options.deprecated }
 }
 
 extension Descriptor.ExtensionRange: ProvidesLocationPath, ProvidesSourceCodeLocation {
-  public func getLocationPath(path: inout IndexPath) {
-    containingType.getLocationPath(path: &path)
-    path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extensionRange)
-    path.append(index)
-  }
+    public func getLocationPath(path: inout IndexPath) {
+        containingType.getLocationPath(path: &path)
+        path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extensionRange)
+        path.append(index)
+    }
 }
 
 extension EnumDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, TypeOrFileProvidesDeprecationComment {
-  public func getLocationPath(path: inout IndexPath) {
-    if let containingType = containingType {
-      containingType.getLocationPath(path: &path)
-      path.append(Google_Protobuf_DescriptorProto.FieldNumbers.enumType)
-    } else {
-      path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.enumType)
+    public func getLocationPath(path: inout IndexPath) {
+        if let containingType = containingType {
+            containingType.getLocationPath(path: &path)
+            path.append(Google_Protobuf_DescriptorProto.FieldNumbers.enumType)
+        } else {
+            path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.enumType)
+        }
+        path.append(index)
     }
-    path.append(index)
-  }
 
-  public var typeName: String { "enum" }
-  public var isDeprecated: Bool { options.deprecated }
+    public var typeName: String { "enum" }
+    public var isDeprecated: Bool { options.deprecated }
 }
 
 extension EnumValueDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, SimpleProvidesDeprecationComment {
-  public func getLocationPath(path: inout IndexPath) {
-    enumType.getLocationPath(path: &path)
-    path.append(Google_Protobuf_EnumDescriptorProto.FieldNumbers.value)
-    path.append(index)
-  }
-
-  public var typeName: String { "enum value" }
-  public var isDeprecated: Bool { options.deprecated }
+    public func getLocationPath(path: inout IndexPath) {
+        enumType.getLocationPath(path: &path)
+        path.append(Google_Protobuf_EnumDescriptorProto.FieldNumbers.value)
+        path.append(index)
+    }
+
+    public var typeName: String { "enum value" }
+    public var isDeprecated: Bool { options.deprecated }
 }
 
 extension OneofDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation {
-  public func getLocationPath(path: inout IndexPath) {
-    containingType.getLocationPath(path: &path)
-    path.append(Google_Protobuf_DescriptorProto.FieldNumbers.oneofDecl)
-    path.append(index)
-  }
+    public func getLocationPath(path: inout IndexPath) {
+        containingType.getLocationPath(path: &path)
+        path.append(Google_Protobuf_DescriptorProto.FieldNumbers.oneofDecl)
+        path.append(index)
+    }
 }
 
 extension FieldDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, ProvidesDeprecationComment {
-  public func getLocationPath(path: inout IndexPath) {
-    if isExtension {
-      if let extensionScope = extensionScope {
-        extensionScope.getLocationPath(path: &path)
-        path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extension)
-      } else {
-        path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.extension)
-      }
-    } else {
-      containingType.getLocationPath(path: &path)
-      path.append(Google_Protobuf_DescriptorProto.FieldNumbers.field)
-    }
-    path.append(index)
-  }
-
-  public func deprecationComment(commentPrefix: String) -> String {
-    // FieldDesciptor can be an extension field or a normal field, so it needs
-    // a custom imply to only look at the file for extentsion fields.
-    if options.deprecated {
-      return "\(commentPrefix) NOTE: This \(isExtension ? "extension field" : "field") was marked as deprecated in the .proto file.\n"
+    public func getLocationPath(path: inout IndexPath) {
+        if isExtension {
+            if let extensionScope = extensionScope {
+                extensionScope.getLocationPath(path: &path)
+                path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extension)
+            } else {
+                path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.extension)
+            }
+        } else {
+            containingType.getLocationPath(path: &path)
+            path.append(Google_Protobuf_DescriptorProto.FieldNumbers.field)
+        }
+        path.append(index)
     }
-    if isExtension && file.options.deprecated {
-      return "\(commentPrefix) NOTE: The whole .proto file that defined this extension field was marked as deprecated.\n"
-    }
-    return String()
-  }
-
-  /// Returns true if the type can be used for a Packed field.
-  static func isPackable(type: Google_Protobuf_FieldDescriptorProto.TypeEnum) -> Bool {
-    // This logic comes from the C++ FieldDescriptor::IsTypePackable() impl.
-    switch type {
-    case .string, .group, .message, .bytes:
-      return false
-    default:
-      return true
+
+    public func deprecationComment(commentPrefix: String) -> String {
+        // FieldDesciptor can be an extension field or a normal field, so it needs
+        // a custom imply to only look at the file for extentsion fields.
+        if options.deprecated {
+            return
+                "\(commentPrefix) NOTE: This \(isExtension ? "extension field" : "field") was marked as deprecated in the .proto file.\n"
+        }
+        if isExtension && file.options.deprecated {
+            return
+                "\(commentPrefix) NOTE: The whole .proto file that defined this extension field was marked as deprecated.\n"
+        }
+        return String()
     }
-  }
-
-  /// Helper to return the name to as the "base" for naming of generated fields.
-  ///
-  /// Groups use the underlying message's name. The way groups are declared in
-  /// proto files, the filed names is derived by lowercasing the Group's name,
-  /// so there are no underscores, etc. to rebuild a camel case name from.
-  var namingBase: String {
-    return internal_isGroupLike ? messageType!.name : name
-  }
-
-  /// Helper to see if this is "group-like". Edition 2024 will likely provide
-  /// a new feature to better deal with this. See upsteam protobuf for more
-  /// details on the problem.
-  ///
-  ///  This models upstream internal::cpp::IsGroupLike().
-  ///
-  ///  TODO(thomasvl): make this `package` instead of `public` and drop the
-  ///  "internal" part from the name when 5.9 is the baseline.
-  public var internal_isGroupLike: Bool {
-    guard type == .group else {
-      return false
+
+    /// Returns true if the type can be used for a Packed field.
+    static func isPackable(type: Google_Protobuf_FieldDescriptorProto.TypeEnum) -> Bool {
+        // This logic comes from the C++ FieldDescriptor::IsTypePackable() impl.
+        switch type {
+        case .string, .group, .message, .bytes:
+            return false
+        default:
+            return true
+        }
     }
-    // `messageType` can't realy be nil once we know it's a group.
-    let messageType = messageType!
 
-    // The original proto2 syntax concept of a group always has a field name
-    // that is the exact lowercasing of the message name.
-    guard name == messageType.name.lowercased() else {
-      return false;
+    /// Helper to return the name to as the "base" for naming of generated fields.
+    ///
+    /// Groups use the underlying message's name. The way groups are declared in
+    /// proto files, the filed names is derived by lowercasing the Group's name,
+    /// so there are no underscores, etc. to rebuild a camel case name from.
+    var namingBase: String {
+        internal_isGroupLike ? messageType!.name : name
     }
 
-    // The message defined by a group is at the same scope as the field. So...
-    if isExtension {
-      if extensionScope == nil {
-        // Top level extension, so the message made by the group has to be the
-        // same file and also a type level type.
-        return messageType.file === file && messageType.containingType == nil
-      } else {
-        // Extension field was scoped to a message, so the group will be also
-        // nested under that same message.
-        return messageType.containingType === extensionScope
-      }
-    } else {
-      // A regular message field, the message made by the group has to be
-      // nested under this same message.
-      return messageType.containingType === containingType
+    /// Helper to see if this is "group-like". Edition 2024 will likely provide
+    /// a new feature to better deal with this. See upsteam protobuf for more
+    /// details on the problem.
+    ///
+    ///  This models upstream internal::cpp::IsGroupLike().
+    ///
+    ///  TODO(thomasvl): make this `package` instead of `public` and drop the
+    ///  "internal" part from the name when 5.9 is the baseline.
+    public var internal_isGroupLike: Bool {
+        guard type == .group else {
+            return false
+        }
+        // `messageType` can't realy be nil once we know it's a group.
+        let messageType = messageType!
+
+        // The original proto2 syntax concept of a group always has a field name
+        // that is the exact lowercasing of the message name.
+        guard name == messageType.name.lowercased() else {
+            return false
+        }
+
+        // The message defined by a group is at the same scope as the field. So...
+        if isExtension {
+            if extensionScope == nil {
+                // Top level extension, so the message made by the group has to be the
+                // same file and also a type level type.
+                return messageType.file === file && messageType.containingType == nil
+            } else {
+                // Extension field was scoped to a message, so the group will be also
+                // nested under that same message.
+                return messageType.containingType === extensionScope
+            }
+        } else {
+            // A regular message field, the message made by the group has to be
+            // nested under this same message.
+            return messageType.containingType === containingType
+        }
     }
-  }
 }
 
 extension ServiceDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, TypeOrFileProvidesDeprecationComment {
-  public func getLocationPath(path: inout IndexPath) {
-    path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.service)
-    path.append(index)
-  }
+    public func getLocationPath(path: inout IndexPath) {
+        path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.service)
+        path.append(index)
+    }
 
-  public var typeName: String { "service" }
-  public var isDeprecated: Bool { options.deprecated }
+    public var typeName: String { "service" }
+    public var isDeprecated: Bool { options.deprecated }
 }
 
 extension MethodDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, SimpleProvidesDeprecationComment {
-  public func getLocationPath(path: inout IndexPath) {
-    service.getLocationPath(path: &path)
-    path.append(Google_Protobuf_ServiceDescriptorProto.FieldNumbers.method)
-    path.append(index)
-  }
-
-  public var typeName: String { "method" }
-  public var isDeprecated: Bool { options.deprecated }
+    public func getLocationPath(path: inout IndexPath) {
+        service.getLocationPath(path: &path)
+        path.append(Google_Protobuf_ServiceDescriptorProto.FieldNumbers.method)
+        path.append(index)
+    }
+
+    public var typeName: String { "method" }
+    public var isDeprecated: Bool { options.deprecated }
 }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 714 - 676
Sources/SwiftProtobufPluginLibrary/Descriptor.swift


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott