Coverage Report

Created: 2026-08-14 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-protobuf/Sources/SwiftProtobuf/NameMap.swift
Line
Count
Source
1
// Sources/SwiftProtobuf/NameMap.swift - Bidirectional number/name mapping
2
//
3
// Copyright (c) 2014 - 2016 Apple Inc. and the project authors
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
//
6
// See LICENSE.txt for license information:
7
// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
8
//
9
// -----------------------------------------------------------------------------
10
11
/// TODO: Right now, only the NameMap and the NameDescription enum
12
/// (which are directly used by the generated code) are public.
13
/// This means that code outside the library has no way to actually
14
/// use this data.  We should develop and publicize a suitable API
15
/// for that purpose.  (Which might be the same as the internal API.)
16
17
/// This must produce exactly the same outputs as the corresponding
18
/// code in the protoc-gen-swift code generator. Changing it will
19
/// break compatibility of the library with older generated code.
20
///
21
/// It does not necessarily need to match protoc's JSON field naming
22
/// logic, however.
23
1.15k
private func toJSONFieldName(_ s: UnsafeBufferPointer<UInt8>) -> String {
24
1.15k
    var result = String.UnicodeScalarView()
25
1.15k
    var capitalizeNext = false
26
18.8k
    for c in s {
27
18.8k
        if c == UInt8(ascii: "_") {
28
2.01k
            capitalizeNext = true
29
16.8k
        } else if capitalizeNext {
30
2.01k
            result.append(Unicode.Scalar(c).uppercasedAssumingASCII)
31
2.01k
            capitalizeNext = false
32
14.7k
        } else {
33
14.7k
            result.append(Unicode.Scalar(c))
34
14.7k
        }
35
18.8k
    }
36
1.15k
    return String(result)
37
1.15k
}
38
#if !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
39
0
private func toJSONFieldName(_ s: StaticString) -> String {
40
0
    guard s.hasPointerRepresentation else {
41
0
        // If it's a single code point, it wouldn't be changed by the above algorithm.
42
0
        // Return it as-is.
43
0
        return s.description
44
0
    }
45
0
    return toJSONFieldName(UnsafeBufferPointer(start: s.utf8Start, count: s.utf8CodeUnitCount))
46
0
}
47
#endif  // !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
48
49
/// Allocate static memory buffers to intern UTF-8
50
/// string data.  Track the buffers and release all of those buffers
51
/// in case we ever get deallocated.
52
private class InternPool {
53
1.04k
    private var interned = [UnsafeRawBufferPointer]()
54
55
1.16k
    func intern(utf8: String.UTF8View) -> UnsafeRawBufferPointer {
56
1.16k
        let mutable = UnsafeMutableRawBufferPointer.allocate(
57
1.16k
            byteCount: utf8.count,
58
1.16k
            alignment: MemoryLayout<UInt8>.alignment
59
1.16k
        )
60
1.16k
        mutable.copyBytes(from: utf8)
61
1.16k
        let immutable = UnsafeRawBufferPointer(mutable)
62
1.16k
        interned.append(immutable)
63
1.16k
        return immutable
64
1.16k
    }
65
66
0
    func intern(utf8Ptr: UnsafeBufferPointer<UInt8>) -> UnsafeRawBufferPointer {
67
0
        let mutable = UnsafeMutableRawBufferPointer.allocate(
68
0
            byteCount: utf8Ptr.count,
69
0
            alignment: MemoryLayout<UInt8>.alignment
70
0
        )
71
0
        mutable.copyBytes(from: utf8Ptr)
72
0
        let immutable = UnsafeRawBufferPointer(mutable)
73
0
        interned.append(immutable)
74
0
        return immutable
75
0
    }
76
77
0
    deinit {
78
0
        for buff in interned {
79
0
            buff.deallocate()
80
0
        }
81
0
    }
82
}
83
84
/// Instructions used in bytecode streams that define proto name mappings.
85
///
86
/// Since field and enum case names are encoded in numeric order, field and case number operands in
87
/// the bytecode are stored as adjacent differences. Most messages/enums use densely packed
88
/// numbers, so we've optimized the opcodes for that; each instruction that takes a single
89
/// field/case number has two forms: one that assumes the next number is +1 from the previous
90
/// number, and a second form that takes an arbitrary delta from the previous number.
91
///
92
/// This has package visibility so that it is also visible to the generator.
93
package enum ProtoNameInstruction: UInt64, CaseIterable {
94
    /// The proto (text format) name and the JSON name are the same string.
95
    ///
96
    /// ## Operands
97
    /// * (Delta only) An integer representing the (delta from the previous) field or enum case
98
    ///   number.
99
    /// * A string containing the single text format and JSON name.
100
    case sameNext = 1
101
    case sameDelta = 2
102
103
    /// The JSON name can be computed from the proto string.
104
    ///
105
    /// ## Operands
106
    /// * (Delta only) An integer representing the (delta from the previous) field or enum case
107
    ///   number.
108
    /// * A string containing the single text format name, from which the JSON name will be
109
    ///   dynamically computed.
110
    case standardNext = 3
111
    case standardDelta = 4
112
113
    /// The JSON and text format names are just different.
114
    ///
115
    /// ## Operands
116
    /// * (Delta only) An integer representing the (delta from the previous) field or enum case
117
    ///   number.
118
    /// * A string containing the text format name.
119
    /// * A string containing the JSON name.
120
    case uniqueNext = 5
121
    case uniqueDelta = 6
122
123
    /// Used for group fields only to represent the message type name of a group.
124
    ///
125
    /// ## Operands
126
    /// * (Delta only) An integer representing the (delta from the previous) field number.
127
    /// * A string containing the (UpperCamelCase by convention) message type name, from which the
128
    ///   text format and JSON names can be derived (lowercase).
129
    case groupNext = 7
130
    case groupDelta = 8
131
132
    /// Used for enum cases only to represent a value's primary proto name (the first defined case)
133
    /// and its aliases. The JSON and text format names for enums are always the same.
134
    ///
135
    /// ## Operands
136
    /// * (Delta only) An integer representing the (delta from the previous) enum case number.
137
    /// * An integer `aliasCount` representing the number of aliases.
138
    /// * A string containing the text format/JSON name (the first defined case with this number).
139
    /// * `aliasCount` strings containing other text format/JSON names that are aliases.
140
    case aliasNext = 9
141
    case aliasDelta = 10
142
143
    /// Represents a reserved name in a proto message.
144
    ///
145
    /// ## Operands
146
    /// * The name of a reserved field.
147
    case reservedName = 11
148
149
    /// Represents a range of reserved field numbers or enum case numbers in a proto message.
150
    ///
151
    /// ## Operands
152
    /// * An integer representing the lower bound (inclusive) of the reserved number range.
153
    /// * An integer representing the delta between the upper bound (exclusive) and the lower bound
154
    ///   of the reserved number range.
155
    case reservedNumbers = 12
156
157
    /// Indicates whether the opcode represents an instruction that has an explicit delta encoded
158
    /// as its first operand.
159
689
    var hasExplicitDelta: Bool {
160
689
        switch self {
161
689
        case .sameDelta, .standardDelta, .uniqueDelta, .groupDelta, .aliasDelta: return true
162
689
        default: return false
163
689
        }
164
689
    }
165
}
166
167
/// An immutable bidirectional mapping between field/enum-case names
168
/// and numbers, used to record field names for text-based
169
/// serialization (JSON and text).  These maps are lazily instantiated
170
/// for each message as needed, so there is no run-time overhead for
171
/// users who do not use text-based serialization formats.
172
public struct _NameMap: ExpressibleByDictionaryLiteral {
173
174
    /// An immutable interned string container.  The `utf8Start` pointer
175
    /// is guaranteed valid for the lifetime of the `NameMap` that you
176
    /// fetched it from.  Since `NameMap`s are only instantiated as
177
    /// immutable static values, that should be the lifetime of the
178
    /// program.
179
    ///
180
    /// Internally, this uses `StaticString` (which refers to a fixed
181
    /// block of UTF-8 data) where possible.  In cases where the string
182
    /// has to be computed, it caches the UTF-8 bytes in an
183
    /// unmovable and immutable heap area.
184
    package struct Name: Hashable, CustomStringConvertible {
185
        #if !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
186
        // This should not be used outside of this file, as it requires
187
        // coordinating the lifecycle with the lifecycle of the pool
188
        // where the raw UTF8 gets interned.
189
0
        fileprivate init(staticString: StaticString, pool: InternPool) {
190
0
            if staticString.hasPointerRepresentation {
191
0
                self.utf8Buffer = UnsafeRawBufferPointer(
192
0
                    start: staticString.utf8Start,
193
0
                    count: staticString.utf8CodeUnitCount
194
0
                )
195
0
            } else {
196
0
                self.utf8Buffer = staticString.withUTF8Buffer { pool.intern(utf8Ptr: $0) }
197
0
            }
198
0
        }
199
        #endif  // !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
200
201
        // This should not be used outside of this file, as it requires
202
        // coordinating the lifecycle with the lifecycle of the pool
203
        // where the raw UTF8 gets interned.
204
1.16k
        fileprivate init(string: String, pool: InternPool) {
205
1.16k
            let utf8 = string.utf8
206
1.16k
            self.utf8Buffer = pool.intern(utf8: utf8)
207
1.16k
        }
208
209
        // This is for building a transient `Name` object sufficient for lookup purposes.
210
        // It MUST NOT be exposed outside of this file.
211
16.2M
        fileprivate init(transientUtf8Buffer: UnsafeRawBufferPointer) {
212
16.2M
            self.utf8Buffer = transientUtf8Buffer
213
16.2M
        }
214
215
        // This is for building a `Name` object from a slice of a bytecode `StaticString`.
216
        // It MUST NOT be exposed outside of this file.
217
1.38k
        fileprivate init(bytecodeUTF8Buffer: UnsafeBufferPointer<UInt8>) {
218
1.38k
            self.utf8Buffer = UnsafeRawBufferPointer(bytecodeUTF8Buffer)
219
1.38k
        }
220
221
        internal let utf8Buffer: UnsafeRawBufferPointer
222
223
50
        public var description: String {
224
50
            String(decoding: self.utf8Buffer, as: UTF8.self)
225
50
        }
226
227
16.2M
        public func hash(into hasher: inout Hasher) {
228
419M
            for byte in utf8Buffer {
229
419M
                hasher.combine(byte)
230
419M
            }
231
16.2M
        }
232
233
15.4M
        public static func == (lhs: Name, rhs: Name) -> Bool {
234
15.4M
            if lhs.utf8Buffer.count != rhs.utf8Buffer.count {
235
13.8M
                return false
236
13.8M
            }
237
1.58M
            return lhs.utf8Buffer.elementsEqual(rhs.utf8Buffer)
238
15.4M
        }
239
    }
240
241
    /// The JSON and proto names for a particular field, enum case, or extension.
242
    internal struct Names {
243
        private(set) var json: Name?
244
        private(set) var proto: Name
245
    }
246
247
    #if !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
248
249
    /// A description of the names for a particular field or enum case.
250
    /// The different forms here let us minimize the amount of string
251
    /// data that we store in the binary.
252
    ///
253
    /// These are only used in the generated code to initialize a NameMap.
254
    public enum NameDescription {
255
256
        /// The proto (text format) name and the JSON name are the same string.
257
        case same(proto: StaticString)
258
259
        /// The JSON name can be computed from the proto string
260
        case standard(proto: StaticString)
261
262
        /// The JSON and text format names are just different.
263
        case unique(proto: StaticString, json: StaticString)
264
265
        /// Used for enum cases only to represent a value's primary proto name (the
266
        /// first defined case) and its aliases. The JSON and text format names for
267
        /// enums are always the same.
268
        case aliased(proto: StaticString, aliases: [StaticString])
269
    }
270
271
    #endif  // !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
272
273
1.04k
    private var internPool = InternPool()
274
275
    /// The mapping from field/enum-case numbers to names.
276
1.04k
    private var numberToNameMap: [Int: Names] = [:]
277
278
    /// The mapping from proto/text names to field/enum-case numbers.
279
1.04k
    private var protoToNumberMap: [Name: Int] = [:]
280
281
    /// The mapping from JSON names to field/enum-case numbers.
282
    /// Note that this also contains all of the proto/text names,
283
    /// as required by Google's spec for protobuf JSON.
284
1.04k
    private var jsonToNumberMap: [Name: Int] = [:]
285
286
    /// The reserved names in for this object. Currently only used for Message to
287
    /// support TextFormat's requirement to skip these names in all cases.
288
1.04k
    private var reservedNames: [String] = []
289
290
    /// The reserved numbers in for this object. Currently only used for Message to
291
    /// support TextFormat's requirement to skip these numbers in all cases.
292
1.04k
    private var reservedRanges: [Range<Int32>] = []
293
294
    /// Creates a new empty field/enum-case name/number mapping.
295
40
    public init() {}
296
297
    #if REMOVE_LEGACY_NAMEMAP_INITIALIZERS
298
299
    // Provide a dummy for ExpressibleByDictionaryLiteral conformance.
300
    public init(dictionaryLiteral elements: (Int, Int)...) {
301
        fatalError("Support compiled out removed")
302
    }
303
304
    #else  // !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
305
306
    /// Build the bidirectional maps between numbers and proto/JSON names.
307
    @available(
308
        *,
309
        deprecated,
310
        message: "Please regenerate your .pb.swift files with the current version of the SwiftProtobuf protoc plugin."
311
    )
312
    public init(
313
        reservedNames: [String],
314
        reservedRanges: [Range<Int32>],
315
        numberNameMappings: KeyValuePairs<Int, NameDescription>
316
0
    ) {
317
0
        self.reservedNames = reservedNames
318
0
        self.reservedRanges = reservedRanges
319
0
320
0
        initHelper(numberNameMappings)
321
0
    }
322
323
    /// Build the bidirectional maps between numbers and proto/JSON names.
324
    @available(
325
        *,
326
        deprecated,
327
        message: "Please regenerate your .pb.swift files with the current version of the SwiftProtobuf protoc plugin."
328
    )
329
0
    public init(dictionaryLiteral elements: (Int, NameDescription)...) {
330
0
        initHelper(elements)
331
0
    }
332
333
    /// Helper to share the building of mappings between the two initializers.
334
    private mutating func initHelper<Pairs: Collection>(
335
        _ elements: Pairs
336
0
    ) where Pairs.Element == (key: Int, value: NameDescription) {
337
0
        for (number, description) in elements {
338
0
            switch description {
339
0
340
0
            case .same(proto: let p):
341
0
                let protoName = Name(staticString: p, pool: internPool)
342
0
                let names = Names(json: protoName, proto: protoName)
343
0
                numberToNameMap[number] = names
344
0
                protoToNumberMap[protoName] = number
345
0
                jsonToNumberMap[protoName] = number
346
0
347
0
            case .standard(proto: let p):
348
0
                let protoName = Name(staticString: p, pool: internPool)
349
0
                let jsonString = toJSONFieldName(p)
350
0
                let jsonName = Name(string: jsonString, pool: internPool)
351
0
                let names = Names(json: jsonName, proto: protoName)
352
0
                numberToNameMap[number] = names
353
0
                protoToNumberMap[protoName] = number
354
0
                jsonToNumberMap[protoName] = number
355
0
                jsonToNumberMap[jsonName] = number
356
0
357
0
            case .unique(proto: let p, json: let j):
358
0
                let jsonName = Name(staticString: j, pool: internPool)
359
0
                let protoName = Name(staticString: p, pool: internPool)
360
0
                let names = Names(json: jsonName, proto: protoName)
361
0
                numberToNameMap[number] = names
362
0
                protoToNumberMap[protoName] = number
363
0
                jsonToNumberMap[protoName] = number
364
0
                jsonToNumberMap[jsonName] = number
365
0
366
0
            case .aliased(proto: let p, let aliases):
367
0
                let protoName = Name(staticString: p, pool: internPool)
368
0
                let names = Names(json: protoName, proto: protoName)
369
0
                numberToNameMap[number] = names
370
0
                protoToNumberMap[protoName] = number
371
0
                jsonToNumberMap[protoName] = number
372
0
                for alias in aliases {
373
0
                    let protoName = Name(staticString: alias, pool: internPool)
374
0
                    protoToNumberMap[protoName] = number
375
0
                    jsonToNumberMap[protoName] = number
376
0
                }
377
0
            }
378
0
        }
379
0
    }
380
381
    #endif  // !REMOVE_LEGACY_NAMEMAP_INITIALIZERS
382
383
117
    public init(bytecode: StaticString) {
384
117
        var previousNumber = 0
385
1.38k
        BytecodeInterpreter<ProtoNameInstruction>(program: bytecode).execute { instruction, reader in
386
1.38k
            func nextNumber() -> Int {
387
1.38k
                let next: Int
388
1.38k
                if instruction.hasExplicitDelta {
389
71
                    next = previousNumber + Int(reader.nextInt32())
390
1.31k
                } else {
391
1.31k
                    next = previousNumber + 1
392
1.31k
                }
393
1.38k
                previousNumber = next
394
1.38k
                return next
395
1.38k
            }
396
1.38k
397
1.38k
            switch instruction {
398
1.38k
            case .sameNext, .sameDelta:
399
216
                let number = nextNumber()
400
216
                let protoName = Name(bytecodeUTF8Buffer: reader.nextNullTerminatedString())
401
216
                numberToNameMap[number] = Names(json: protoName, proto: protoName)
402
216
                protoToNumberMap[protoName] = number
403
216
                jsonToNumberMap[protoName] = number
404
1.38k
405
1.38k
            case .standardNext, .standardDelta:
406
1.15k
                let number = nextNumber()
407
1.15k
                let protoNameBuffer = reader.nextNullTerminatedString()
408
1.15k
                let protoName = Name(bytecodeUTF8Buffer: protoNameBuffer)
409
1.15k
                let jsonString = toJSONFieldName(protoNameBuffer)
410
1.15k
                let jsonName = Name(string: jsonString, pool: internPool)
411
1.15k
                numberToNameMap[number] = Names(json: jsonName, proto: protoName)
412
1.15k
                protoToNumberMap[protoName] = number
413
1.15k
                jsonToNumberMap[protoName] = number
414
1.15k
                jsonToNumberMap[jsonName] = number
415
1.38k
416
1.38k
            case .uniqueNext, .uniqueDelta:
417
0
                let number = nextNumber()
418
0
                let protoName = Name(bytecodeUTF8Buffer: reader.nextNullTerminatedString())
419
0
                let jsonName = Name(bytecodeUTF8Buffer: reader.nextNullTerminatedString())
420
0
                numberToNameMap[number] = Names(json: jsonName, proto: protoName)
421
0
                protoToNumberMap[protoName] = number
422
0
                jsonToNumberMap[protoName] = number
423
0
                jsonToNumberMap[jsonName] = number
424
1.38k
425
1.38k
            case .groupNext, .groupDelta:
426
12
                let number = nextNumber()
427
12
                let protoNameBuffer = reader.nextNullTerminatedString()
428
12
                let protoName = Name(bytecodeUTF8Buffer: protoNameBuffer)
429
12
                protoToNumberMap[protoName] = number
430
12
                jsonToNumberMap[protoName] = number
431
12
432
12
                let lowercaseName: Name
433
12
                let hasUppercase = protoNameBuffer.contains { (UInt8(ascii: "A")...UInt8(ascii: "Z")).contains($0) }
434
12
                if hasUppercase {
435
12
                    lowercaseName = Name(
436
12
                        string: String(decoding: protoNameBuffer, as: UTF8.self).lowercased(),
437
12
                        pool: internPool
438
12
                    )
439
12
                    protoToNumberMap[lowercaseName] = number
440
12
                    jsonToNumberMap[lowercaseName] = number
441
12
                } else {
442
0
                    // No need to convert and intern a separate copy of the string
443
0
                    // if it would be identical.
444
0
                    lowercaseName = protoName
445
0
                }
446
12
                numberToNameMap[number] = Names(json: lowercaseName, proto: protoName)
447
1.38k
448
1.38k
            case .aliasNext, .aliasDelta:
449
0
                let number = nextNumber()
450
0
                let protoName = Name(bytecodeUTF8Buffer: reader.nextNullTerminatedString())
451
0
                numberToNameMap[number] = Names(json: protoName, proto: protoName)
452
0
                protoToNumberMap[protoName] = number
453
0
                jsonToNumberMap[protoName] = number
454
0
                for alias in reader.nextNullTerminatedStringArray() {
455
0
                    let protoName = Name(bytecodeUTF8Buffer: alias)
456
0
                    protoToNumberMap[protoName] = number
457
0
                    jsonToNumberMap[protoName] = number
458
0
                }
459
1.38k
460
1.38k
            case .reservedName:
461
0
                let name = String(decoding: reader.nextNullTerminatedString(), as: UTF8.self)
462
0
                reservedNames.append(name)
463
1.38k
464
1.38k
            case .reservedNumbers:
465
0
                let lowerBound = reader.nextInt32()
466
0
                let upperBound = lowerBound + reader.nextInt32()
467
0
                reservedRanges.append(lowerBound..<upperBound)
468
1.38k
            }
469
1.38k
        }
470
117
    }
471
472
    /// Returns the name bundle for the field/enum-case with the given number, or
473
    /// `nil` if there is no match.
474
51.0M
    internal func names(for number: Int) -> Names? {
475
51.0M
        numberToNameMap[number]
476
51.0M
    }
477
478
    /// Returns the field/enum-case number that has the given JSON name,
479
    /// or `nil` if there is no match.
480
    ///
481
    /// This is used by the Text format parser to look up field or enum
482
    /// names using a direct reference to the un-decoded UTF8 bytes.
483
76.4k
    internal func number(forProtoName raw: UnsafeRawBufferPointer) -> Int? {
484
76.4k
        let n = Name(transientUtf8Buffer: raw)
485
76.4k
        return protoToNumberMap[n]
486
76.4k
    }
487
488
    /// Returns the field/enum-case number that has the given JSON name,
489
    /// or `nil` if there is no match.
490
    ///
491
    /// This accepts a regular `String` and is used in JSON parsing
492
    /// only when a field name or enum name was decoded from a string
493
    /// containing backslash escapes.
494
    ///
495
    /// JSON parsing must interpret *both* the JSON name of the
496
    /// field/enum-case provided by the descriptor *as well as* its
497
    /// original proto/text name.
498
277k
    internal func number(forJSONName name: String) -> Int? {
499
277k
        let utf8 = Array(name.utf8)
500
309k
        return utf8.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
501
309k
            let n = Name(transientUtf8Buffer: buffer)
502
309k
            return jsonToNumberMap[n]
503
309k
        }
504
277k
    }
505
506
    /// Returns the field/enum-case number that has the given JSON name,
507
    /// or `nil` if there is no match.
508
    ///
509
    /// This is used by the JSON parser when a field name or enum name
510
    /// required no special processing.  As a result, we can avoid
511
    /// copying the name and look up the number using a direct reference
512
    /// to the un-decoded UTF8 bytes.
513
6.32M
    internal func number(forJSONName raw: UnsafeRawBufferPointer) -> Int? {
514
6.32M
        let n = Name(transientUtf8Buffer: raw)
515
6.32M
        return jsonToNumberMap[n]
516
6.32M
    }
517
518
    /// Returns all proto names
519
0
    internal var names: [Name] {
520
0
        numberToNameMap.map(\.value.proto)
521
0
    }
522
523
    /// Returns if the given name was reserved.
524
481
    internal func isReserved(name: UnsafeRawBufferPointer) -> Bool {
525
481
        guard !reservedNames.isEmpty,
526
481
            let baseAddress = name.baseAddress,
527
481
            let s = utf8ToString(bytes: baseAddress, count: name.count)
528
481
        else {
529
481
            return false
530
481
        }
531
0
        return reservedNames.contains(s)
532
481
    }
533
534
    /// Returns if the given number was reserved.
535
221
    internal func isReserved(number: Int32) -> Bool {
536
221
        for range in reservedRanges {
537
0
            if range.contains(number) {
538
0
                return true
539
0
            }
540
221
        }
541
221
        return false
542
221
    }
543
}
544
545
// The `_NameMap` (and supporting types) are only mutated during their initial
546
// creation, then for the lifetime of the a process they are constant. Swift
547
// 5.10 flags the generated `_protobuf_nameMap` usages as a problem
548
// (https://github.com/apple/swift-protobuf/issues/1560) so this silences those
549
// warnings since the usage has been deemed safe.
550
//
551
// https://github.com/apple/swift-protobuf/issues/1561 is also opened to revisit
552
// the `_NameMap` generally as it dates back to the days before Swift perferred
553
// the UTF-8 internal encoding.
554
extension _NameMap: Sendable {}
555
extension _NameMap.Name: @unchecked Sendable {}
556
extension InternPool: @unchecked Sendable {}