Coverage Report

Created: 2025-07-04 06:57

/src/swift-protobuf/Sources/SwiftProtobuf/TextFormatEncoder.swift
Line
Count
Source (jump to first uncovered line)
1
// Sources/SwiftProtobuf/TextFormatEncoder.swift - Text format encoding support
2
//
3
// Copyright (c) 2014 - 2019 Apple Inc. and the project authors
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
//
6
// See LICENSE.txt for license information:
7
// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
8
//
9
// -----------------------------------------------------------------------------
10
///
11
/// Text format serialization engine.
12
///
13
// -----------------------------------------------------------------------------
14
15
import Foundation
16
17
private let asciiSpace = UInt8(ascii: " ")
18
private let asciiColon = UInt8(ascii: ":")
19
private let asciiComma = UInt8(ascii: ",")
20
private let asciiMinus = UInt8(ascii: "-")
21
private let asciiBackslash = UInt8(ascii: "\\")
22
private let asciiDoubleQuote = UInt8(ascii: "\"")
23
private let asciiZero = UInt8(ascii: "0")
24
private let asciiOpenCurlyBracket = UInt8(ascii: "{")
25
private let asciiCloseCurlyBracket = UInt8(ascii: "}")
26
private let asciiOpenSquareBracket = UInt8(ascii: "[")
27
private let asciiCloseSquareBracket = UInt8(ascii: "]")
28
private let asciiNewline = UInt8(ascii: "\n")
29
private let asciiUpperA = UInt8(ascii: "A")
30
31
private let tabSize = 2
32
private let tab = [UInt8](repeating: asciiSpace, count: tabSize)
33
34
/// TextFormatEncoder has no public members.
35
internal struct TextFormatEncoder {
36
22.0k
    private var data = [UInt8]()
37
22.0k
    private var indentString: [UInt8] = []
38
    var stringResult: String {
39
12.1k
        get {
40
12.1k
            String(decoding: data, as: UTF8.self)
41
12.1k
        }
42
    }
43
44
564M
    internal mutating func append(staticText: StaticString) {
45
564M
        let buff = UnsafeBufferPointer(start: staticText.utf8Start, count: staticText.utf8CodeUnitCount)
46
564M
        data.append(contentsOf: buff)
47
564M
    }
48
49
0
    internal mutating func append(name: _NameMap.Name) {
50
0
        data.append(contentsOf: name.utf8Buffer)
51
0
    }
52
53
0
    internal mutating func append(bytes: [UInt8]) {
54
0
        data.append(contentsOf: bytes)
55
0
    }
56
57
99.6k
    private mutating func append(text: String) {
58
99.6k
        data.append(contentsOf: text.utf8)
59
99.6k
    }
60
61
12.1k
    init() {}
62
63
290M
    internal mutating func indent() {
64
290M
        data.append(contentsOf: indentString)
65
290M
    }
66
67
5.88M
    mutating func emitFieldName(name: UnsafeRawBufferPointer) {
68
5.88M
        indent()
69
5.88M
        data.append(contentsOf: name)
70
5.88M
    }
71
72
475k
    mutating func emitFieldName(name: StaticString) {
73
475k
        let buff = UnsafeRawBufferPointer(start: name.utf8Start, count: name.utf8CodeUnitCount)
74
475k
        emitFieldName(name: buff)
75
475k
    }
76
77
876k
    mutating func emitFieldName(name: [UInt8]) {
78
876k
        indent()
79
876k
        data.append(contentsOf: name)
80
876k
    }
81
82
72.5k
    mutating func emitExtensionFieldName(name: String) {
83
72.5k
        indent()
84
72.5k
        data.append(asciiOpenSquareBracket)
85
72.5k
        append(text: name)
86
72.5k
        data.append(asciiCloseSquareBracket)
87
72.5k
    }
88
89
17.6M
    mutating func emitFieldNumber(number: Int) {
90
17.6M
        indent()
91
17.6M
        appendUInt(value: UInt64(number))
92
17.6M
    }
93
94
70.4M
    mutating func startRegularField() {
95
70.4M
        append(staticText: ": ")
96
70.4M
    }
97
125M
    mutating func endRegularField() {
98
125M
        data.append(asciiNewline)
99
125M
    }
100
101
    // In Text format, a message-valued field writes the name
102
    // without a trailing colon:
103
    //    name_of_field {key: value key2: value2}
104
12.2M
    mutating func startMessageField() {
105
12.2M
        append(staticText: " {\n")
106
12.2M
        indentString.append(contentsOf: tab)
107
12.2M
    }
108
109
48.8M
    mutating func endMessageField() {
110
48.8M
        indentString.removeLast(tabSize)
111
48.8M
        indent()
112
48.8M
        append(staticText: "}\n")
113
48.8M
    }
114
115
151k
    mutating func startArray() {
116
151k
        data.append(asciiOpenSquareBracket)
117
151k
    }
118
119
566k
    mutating func arraySeparator() {
120
566k
        append(staticText: ", ")
121
566k
    }
122
123
151k
    mutating func endArray() {
124
151k
        data.append(asciiCloseSquareBracket)
125
151k
    }
126
127
60.0k
    mutating func putEnumValue<E: Enum>(value: E) {
128
60.0k
        if let name = value.name {
129
53.8k
            data.append(contentsOf: name.utf8Buffer)
130
60.0k
        } else {
131
6.19k
            appendInt(value: Int64(value.rawValue))
132
60.0k
        }
133
60.0k
    }
134
135
265k
    mutating func putFloatValue(value: Float) {
136
265k
        if value.isNaN {
137
6.92k
            append(staticText: "nan")
138
265k
        } else if !value.isFinite {
139
98.2k
            if value < 0 {
140
33.6k
                append(staticText: "-inf")
141
98.2k
            } else {
142
64.5k
                append(staticText: "inf")
143
98.2k
            }
144
265k
        } else {
145
167k
            data.append(contentsOf: value.debugDescription.utf8)
146
265k
        }
147
265k
    }
148
149
966k
    mutating func putDoubleValue(value: Double) {
150
966k
        if value.isNaN {
151
3.76k
            append(staticText: "nan")
152
966k
        } else if !value.isFinite {
153
755k
            if value < 0 {
154
1.27k
                append(staticText: "-inf")
155
755k
            } else {
156
753k
                append(staticText: "inf")
157
755k
            }
158
966k
        } else {
159
210k
            data.append(contentsOf: value.debugDescription.utf8)
160
966k
        }
161
966k
    }
162
163
25.1M
    private mutating func appendUInt(value: UInt64) {
164
25.1M
        if value >= 1000 {
165
294k
            appendUInt(value: value / 1000)
166
25.1M
        }
167
25.1M
        if value >= 100 {
168
3.58M
            data.append(asciiZero + UInt8((value / 100) % 10))
169
25.1M
        }
170
25.1M
        if value >= 10 {
171
10.5M
            data.append(asciiZero + UInt8((value / 10) % 10))
172
25.1M
        }
173
25.1M
        data.append(asciiZero + UInt8(value % 10))
174
25.1M
    }
175
2.61M
    private mutating func appendInt(value: Int64) {
176
2.61M
        if value < 0 {
177
649k
            data.append(asciiMinus)
178
649k
            // This is the twos-complement negation of value,
179
649k
            // computed in a way that won't overflow a 64-bit
180
649k
            // signed integer.
181
649k
            appendUInt(value: 1 + ~UInt64(bitPattern: value))
182
2.61M
        } else {
183
1.96M
            appendUInt(value: UInt64(bitPattern: value))
184
2.61M
        }
185
2.61M
    }
186
187
2.40M
    mutating func putInt64(value: Int64) {
188
2.40M
        appendInt(value: value)
189
2.40M
    }
190
191
23.9M
    mutating func putUInt64(value: UInt64) {
192
23.9M
        appendUInt(value: value)
193
23.9M
    }
194
195
10.6M
    mutating func appendUIntHex(value: UInt64, digits: Int) {
196
10.6M
        if digits == 0 {
197
906k
            append(staticText: "0x")
198
10.6M
        } else {
199
9.76M
            appendUIntHex(value: value >> 4, digits: digits - 1)
200
9.76M
            let d = UInt8(truncatingIfNeeded: value % 16)
201
9.76M
            data.append(d < 10 ? asciiZero + d : asciiUpperA + d - 10)
202
10.6M
        }
203
10.6M
    }
204
205
906k
    mutating func putUInt64Hex(value: UInt64, digits: Int) {
206
906k
        appendUIntHex(value: value, digits: digits)
207
906k
    }
208
209
159k
    mutating func putBoolValue(value: Bool) {
210
159k
        append(staticText: value ? "true" : "false")
211
159k
    }
212
213
150k
    mutating func putStringValue(value: String) {
214
150k
        data.append(asciiDoubleQuote)
215
21.6M
        for c in value.unicodeScalars {
216
21.6M
            switch c.value {
217
21.6M
            // Special two-byte escapes
218
21.6M
            case 8:
219
74.3k
                append(staticText: "\\b")
220
21.6M
            case 9:
221
26.7k
                append(staticText: "\\t")
222
21.6M
            case 10:
223
22.4k
                append(staticText: "\\n")
224
21.6M
            case 11:
225
78.9k
                append(staticText: "\\v")
226
21.6M
            case 12:
227
229k
                append(staticText: "\\f")
228
21.6M
            case 13:
229
7.24k
                append(staticText: "\\r")
230
21.6M
            case 34:
231
4.55k
                append(staticText: "\\\"")
232
21.6M
            case 92:
233
2.64k
                append(staticText: "\\\\")
234
21.6M
            case 0...31, 127:  // Octal form for C0 control chars
235
6.42M
                data.append(asciiBackslash)
236
6.42M
                data.append(asciiZero + UInt8(c.value / 64))
237
6.42M
                data.append(asciiZero + UInt8(c.value / 8 % 8))
238
6.42M
                data.append(asciiZero + UInt8(c.value % 8))
239
21.6M
            case 0...127:  // ASCII
240
9.90M
                data.append(UInt8(truncatingIfNeeded: c.value))
241
21.6M
            case 0x80...0x7ff:
242
12.0k
                data.append(0xc0 + UInt8(c.value / 64))
243
12.0k
                data.append(0x80 + UInt8(c.value % 64))
244
21.6M
            case 0x800...0xffff:
245
4.91M
                data.append(0xe0 + UInt8(truncatingIfNeeded: c.value >> 12))
246
4.91M
                data.append(0x80 + UInt8(truncatingIfNeeded: (c.value >> 6) & 0x3f))
247
4.91M
                data.append(0x80 + UInt8(truncatingIfNeeded: c.value & 0x3f))
248
21.6M
            default:
249
4.81k
                data.append(0xf0 + UInt8(truncatingIfNeeded: c.value >> 18))
250
4.81k
                data.append(0x80 + UInt8(truncatingIfNeeded: (c.value >> 12) & 0x3f))
251
4.81k
                data.append(0x80 + UInt8(truncatingIfNeeded: (c.value >> 6) & 0x3f))
252
4.81k
                data.append(0x80 + UInt8(truncatingIfNeeded: c.value & 0x3f))
253
21.6M
            }
254
21.6M
        }
255
150k
        data.append(asciiDoubleQuote)
256
150k
    }
257
258
225k
    mutating func putBytesValue(value: Data) {
259
225k
        data.append(asciiDoubleQuote)
260
256k
        value.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
261
256k
            if let p = body.baseAddress, body.count > 0 {
262
80.1M
                for i in 0..<body.count {
263
80.1M
                    let c = p[i]
264
80.1M
                    switch c {
265
80.1M
                    // Special two-byte escapes
266
80.1M
                    case 8:
267
615k
                        append(staticText: "\\b")
268
80.1M
                    case 9:
269
62.1k
                        append(staticText: "\\t")
270
80.1M
                    case 10:
271
122k
                        append(staticText: "\\n")
272
80.1M
                    case 11:
273
25.8k
                        append(staticText: "\\v")
274
80.1M
                    case 12:
275
8.02k
                        append(staticText: "\\f")
276
80.1M
                    case 13:
277
5.37k
                        append(staticText: "\\r")
278
80.1M
                    case 34:
279
9.64k
                        append(staticText: "\\\"")
280
80.1M
                    case 92:
281
1.87k
                        append(staticText: "\\\\")
282
80.1M
                    case 32...126:  // printable ASCII
283
11.4M
                        data.append(c)
284
80.1M
                    default:  // Octal form for non-printable chars
285
67.9M
                        data.append(asciiBackslash)
286
67.9M
                        data.append(asciiZero + UInt8(c / 64))
287
67.9M
                        data.append(asciiZero + UInt8(c / 8 % 8))
288
67.9M
                        data.append(asciiZero + UInt8(c % 8))
289
80.1M
                    }
290
80.1M
                }
291
256k
            }
292
256k
        }
293
225k
        data.append(asciiDoubleQuote)
294
225k
    }
295
}