/src/swift-protobuf/Sources/SwiftProtobuf/JSONScanner.swift
Line | Count | Source |
1 | | // Sources/SwiftProtobuf/JSONScanner.swift - JSON format decoding |
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 | | /// JSON format decoding engine. |
12 | | /// |
13 | | // ----------------------------------------------------------------------------- |
14 | | |
15 | | #if canImport(FoundationEssentials) |
16 | | import FoundationEssentials |
17 | | #else |
18 | | import Foundation |
19 | | #endif |
20 | | |
21 | | private let asciiBell = UInt8(7) |
22 | | private let asciiBackspace = UInt8(8) |
23 | | private let asciiTab = UInt8(9) |
24 | | private let asciiNewLine = UInt8(10) |
25 | | private let asciiVerticalTab = UInt8(11) |
26 | | private let asciiFormFeed = UInt8(12) |
27 | | private let asciiCarriageReturn = UInt8(13) |
28 | | private let asciiZero = UInt8(ascii: "0") |
29 | | private let asciiOne = UInt8(ascii: "1") |
30 | | private let asciiSeven = UInt8(ascii: "7") |
31 | | private let asciiNine = UInt8(ascii: "9") |
32 | | private let asciiColon = UInt8(ascii: ":") |
33 | | private let asciiPeriod = UInt8(ascii: ".") |
34 | | private let asciiPlus = UInt8(ascii: "+") |
35 | | private let asciiComma = UInt8(ascii: ",") |
36 | | private let asciiSemicolon = UInt8(ascii: ";") |
37 | | private let asciiDoubleQuote = UInt8(ascii: "\"") |
38 | | private let asciiSingleQuote = UInt8(ascii: "\'") |
39 | | private let asciiBackslash = UInt8(ascii: "\\") |
40 | | private let asciiForwardSlash = UInt8(ascii: "/") |
41 | | private let asciiHash = UInt8(ascii: "#") |
42 | | private let asciiEqualSign = UInt8(ascii: "=") |
43 | | private let asciiUnderscore = UInt8(ascii: "_") |
44 | | private let asciiQuestionMark = UInt8(ascii: "?") |
45 | | private let asciiSpace = UInt8(ascii: " ") |
46 | | private let asciiOpenSquareBracket = UInt8(ascii: "[") |
47 | | private let asciiCloseSquareBracket = UInt8(ascii: "]") |
48 | | private let asciiOpenCurlyBracket = UInt8(ascii: "{") |
49 | | private let asciiCloseCurlyBracket = UInt8(ascii: "}") |
50 | | private let asciiOpenAngleBracket = UInt8(ascii: "<") |
51 | | private let asciiCloseAngleBracket = UInt8(ascii: ">") |
52 | | private let asciiMinus = UInt8(ascii: "-") |
53 | | private let asciiLowerA = UInt8(ascii: "a") |
54 | | private let asciiUpperA = UInt8(ascii: "A") |
55 | | private let asciiLowerB = UInt8(ascii: "b") |
56 | | private let asciiLowerE = UInt8(ascii: "e") |
57 | | private let asciiUpperE = UInt8(ascii: "E") |
58 | | private let asciiLowerF = UInt8(ascii: "f") |
59 | | private let asciiUpperI = UInt8(ascii: "I") |
60 | | private let asciiLowerL = UInt8(ascii: "l") |
61 | | private let asciiLowerN = UInt8(ascii: "n") |
62 | | private let asciiUpperN = UInt8(ascii: "N") |
63 | | private let asciiLowerR = UInt8(ascii: "r") |
64 | | private let asciiLowerS = UInt8(ascii: "s") |
65 | | private let asciiLowerT = UInt8(ascii: "t") |
66 | | private let asciiLowerU = UInt8(ascii: "u") |
67 | | private let asciiLowerZ = UInt8(ascii: "z") |
68 | | private let asciiUpperZ = UInt8(ascii: "Z") |
69 | | |
70 | 61.5k | private func fromHexDigit(_ c: UnicodeScalar) -> UInt32? { |
71 | 61.5k | let n = c.value |
72 | 61.5k | if n >= 48 && n <= 57 { |
73 | 54.7k | return UInt32(n - 48) |
74 | 54.7k | } |
75 | 6.82k | switch n { |
76 | 6.82k | case 65, 97: return 10 |
77 | 6.82k | case 66, 98: return 11 |
78 | 6.82k | case 67, 99: return 12 |
79 | 6.82k | case 68, 100: return 13 |
80 | 6.82k | case 69, 101: return 14 |
81 | 6.82k | case 70, 102: return 15 |
82 | 6.82k | default: |
83 | 75 | return nil |
84 | 6.82k | } |
85 | 6.82k | } |
86 | | |
87 | | // Decode both the RFC 4648 section 4 Base 64 encoding and the RFC |
88 | | // 4648 section 5 Base 64 variant. The section 5 variant is also |
89 | | // known as "base64url" or the "URL-safe alphabet". |
90 | | // Note that both "-" and "+" decode to 62 and "/" and "_" both |
91 | | // decode as 63. |
92 | | // swift-format-ignore: NoBlockComments |
93 | | let base64Values: [Int] = [ |
94 | | /* 0x00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
95 | | /* 0x10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
96 | | /* 0x20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, |
97 | | /* 0x30 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, |
98 | | /* 0x40 */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
99 | | /* 0x50 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, |
100 | | /* 0x60 */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
101 | | /* 0x70 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, |
102 | | /* 0x80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
103 | | /* 0x90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
104 | | /* 0xa0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
105 | | /* 0xb0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
106 | | /* 0xc0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
107 | | /* 0xd0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
108 | | /* 0xe0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
109 | | /* 0xf0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
110 | | ] |
111 | | |
112 | | /// Returns a `Data` value containing bytes equivalent to the given |
113 | | /// Base64-encoded string, or nil if the conversion fails. |
114 | | /// |
115 | | /// Notes on Google's implementation (Base64Unescape() in strutil.cc): |
116 | | /// * Google's C++ implementation accepts arbitrary whitespace |
117 | | /// mixed in with the base-64 characters |
118 | | /// * Google's C++ implementation ignores missing '=' characters |
119 | | /// but if present, there must be the exact correct number of them. |
120 | | /// * The conformance test requires us to accept both standard RFC4648 |
121 | | /// Base 64 encoding and the "URL and Filename Safe Alphabet" variant. |
122 | | /// |
123 | | private func parseBytes( |
124 | | source: UnsafeRawBufferPointer, |
125 | | index: inout UnsafeRawBufferPointer.Index, |
126 | | end: UnsafeRawBufferPointer.Index |
127 | 12.6k | ) throws -> Data { |
128 | 12.6k | let c = source[index] |
129 | 12.6k | if c != asciiDoubleQuote { |
130 | 26 | throw JSONDecodingError.malformedString |
131 | 12.6k | } |
132 | 12.6k | source.formIndex(after: &index) |
133 | 12.6k | |
134 | 12.6k | // Count the base-64 digits |
135 | 12.6k | // Ignore most unrecognized characters in this first pass, |
136 | 12.6k | // stop at the closing double quote. |
137 | 12.6k | let digitsStart = index |
138 | 12.6k | var rawChars = 0 |
139 | 12.6k | var sawSection4Characters = false |
140 | 12.6k | var sawSection5Characters = false |
141 | 10.6M | while index != end { |
142 | 10.6M | var digit = source[index] |
143 | 10.6M | if digit == asciiDoubleQuote { |
144 | 12.5k | break |
145 | 10.6M | } |
146 | 10.6M | |
147 | 10.6M | if digit == asciiBackslash { |
148 | 277 | source.formIndex(after: &index) |
149 | 277 | if index == end { |
150 | 2 | throw JSONDecodingError.malformedString |
151 | 275 | } |
152 | 275 | let escaped = source[index] |
153 | 275 | switch escaped { |
154 | 275 | case asciiLowerU: |
155 | 2 | // TODO: Parse hex escapes such as \u0041. Note that |
156 | 2 | // such escapes are going to be extremely rare, so |
157 | 2 | // there's little point in optimizing for them. |
158 | 2 | throw JSONDecodingError.malformedString |
159 | 275 | case asciiForwardSlash: |
160 | 270 | digit = escaped |
161 | 275 | default: |
162 | 3 | // Reject \b \f \n \r \t \" or \\ and all illegal escapes |
163 | 3 | throw JSONDecodingError.malformedString |
164 | 275 | } |
165 | 10.6M | } |
166 | 10.6M | |
167 | 10.6M | if digit == asciiPlus || digit == asciiForwardSlash { |
168 | 4.31k | sawSection4Characters = true |
169 | 10.6M | } else if digit == asciiMinus || digit == asciiUnderscore { |
170 | 3.77k | sawSection5Characters = true |
171 | 3.77k | } |
172 | 10.6M | let k = base64Values[Int(digit)] |
173 | 10.6M | if k >= 0 { |
174 | 10.3M | rawChars += 1 |
175 | 10.3M | } |
176 | 10.6M | source.formIndex(after: &index) |
177 | 10.6M | } |
178 | 12.6k | |
179 | 12.6k | // We reached the end without seeing the close quote |
180 | 12.6k | if index == end { |
181 | 85 | throw JSONDecodingError.malformedString |
182 | 12.5k | } |
183 | 12.5k | // Reject mixed encodings. |
184 | 12.5k | if sawSection4Characters && sawSection5Characters { |
185 | 4 | throw JSONDecodingError.malformedString |
186 | 12.5k | } |
187 | 12.5k | |
188 | 12.5k | // Allocate a Data object of exactly the right size |
189 | 12.5k | var value = Data(count: rawChars * 3 / 4) |
190 | 12.5k | |
191 | 12.5k | // Scan the digits again and populate the Data object. |
192 | 12.5k | // In this pass, we check for (and fail) if there are |
193 | 12.5k | // unexpected characters. But we don't check for end-of-input, |
194 | 12.5k | // because the loop above already verified that there was |
195 | 12.5k | // a closing double quote. |
196 | 12.5k | index = digitsStart |
197 | 12.5k | try value.withUnsafeMutableBytes { |
198 | 12.5k | (body: UnsafeMutableRawBufferPointer) in |
199 | 12.5k | if var p = body.baseAddress, body.count > 0 { |
200 | 6.03k | var n = 0 |
201 | 6.03k | var chars = 0 // # chars in current group |
202 | 6.03k | var padding = 0 // # padding '=' chars |
203 | 8.66M | digits: while true { |
204 | 8.66M | let digit = source[index] |
205 | 8.66M | var k = base64Values[Int(digit)] |
206 | 8.66M | if k < 0 { |
207 | 137k | switch digit { |
208 | 137k | case asciiDoubleQuote: |
209 | 5.66k | break digits |
210 | 137k | case asciiBackslash: |
211 | 122 | source.formIndex(after: &index) |
212 | 122 | let escaped = source[index] |
213 | 122 | switch escaped { |
214 | 122 | case asciiForwardSlash: |
215 | 122 | k = base64Values[Int(escaped)] |
216 | 122 | default: |
217 | 0 | // Note: Invalid backslash escapes were caught |
218 | 0 | // above; we should never get here. |
219 | 0 | throw JSONDecodingError.malformedString |
220 | 122 | } |
221 | 137k | case asciiSpace: |
222 | 130k | source.formIndex(after: &index) |
223 | 130k | continue digits |
224 | 137k | case asciiEqualSign: // Count padding |
225 | 26.0k | while true { |
226 | 26.0k | switch source[index] { |
227 | 26.0k | case asciiDoubleQuote: |
228 | 318 | break digits |
229 | 26.0k | case asciiSpace: |
230 | 24.0k | break |
231 | 26.0k | case 61: |
232 | 1.70k | padding += 1 |
233 | 26.0k | default: // Only '=' and whitespace permitted |
234 | 25 | throw JSONDecodingError.malformedString |
235 | 26.0k | } |
236 | 25.7k | source.formIndex(after: &index) |
237 | 25.7k | } |
238 | 137k | default: |
239 | 23 | throw JSONDecodingError.malformedString |
240 | 137k | } |
241 | 8.53M | } |
242 | 8.53M | n <<= 6 |
243 | 8.53M | n |= k |
244 | 8.53M | chars += 1 |
245 | 8.53M | if chars == 4 { |
246 | 2.13M | p[0] = UInt8(truncatingIfNeeded: n >> 16) |
247 | 2.13M | p[1] = UInt8(truncatingIfNeeded: n >> 8) |
248 | 2.13M | p[2] = UInt8(truncatingIfNeeded: n) |
249 | 2.13M | p += 3 |
250 | 2.13M | chars = 0 |
251 | 2.13M | n = 0 |
252 | 2.13M | } |
253 | 8.53M | source.formIndex(after: &index) |
254 | 8.53M | } |
255 | 5.98k | switch chars { |
256 | 5.98k | case 3: |
257 | 1.58k | p[0] = UInt8(truncatingIfNeeded: n >> 10) |
258 | 1.58k | p[1] = UInt8(truncatingIfNeeded: n >> 2) |
259 | 1.58k | if padding == 1 || padding == 0 { |
260 | 1.58k | return |
261 | 1.58k | } |
262 | 5.98k | case 2: |
263 | 2.23k | p[0] = UInt8(truncatingIfNeeded: n >> 4) |
264 | 2.23k | if padding == 2 || padding == 0 { |
265 | 2.22k | return |
266 | 2.22k | } |
267 | 5.98k | case 0: |
268 | 2.14k | if padding == 0 { |
269 | 2.14k | return |
270 | 2.14k | } |
271 | 5.98k | default: |
272 | 12 | break |
273 | 5.98k | } |
274 | 30 | throw JSONDecodingError.malformedString |
275 | 6.48k | } |
276 | 12.4k | } |
277 | 12.4k | source.formIndex(after: &index) |
278 | 12.4k | return value |
279 | 12.6k | } |
280 | | |
281 | | // JSON encoding allows a variety of \-escapes, including |
282 | | // escaping UTF-16 code points (which may be surrogate pairs). |
283 | 14.9k | private func decodeString(_ s: String) -> String? { |
284 | 14.9k | var out = String.UnicodeScalarView() |
285 | 14.9k | var chars = s.unicodeScalars.makeIterator() |
286 | 1.20M | while let c = chars.next() { |
287 | 1.20M | switch c.value { |
288 | 1.20M | case UInt32(asciiBackslash): // backslash |
289 | 17.4k | if let escaped = chars.next() { |
290 | 17.4k | switch escaped.value { |
291 | 17.4k | case UInt32(asciiLowerU): // "\u" |
292 | 14.9k | // Exactly 4 hex digits: |
293 | 14.9k | if let digit1 = chars.next(), |
294 | 14.9k | let d1 = fromHexDigit(digit1), |
295 | 14.9k | let digit2 = chars.next(), |
296 | 14.9k | let d2 = fromHexDigit(digit2), |
297 | 14.9k | let digit3 = chars.next(), |
298 | 14.9k | let d3 = fromHexDigit(digit3), |
299 | 14.9k | let digit4 = chars.next(), |
300 | 14.9k | let d4 = fromHexDigit(digit4) |
301 | 14.9k | { |
302 | 14.7k | let codePoint = ((d1 * 16 + d2) * 16 + d3) * 16 + d4 |
303 | 14.7k | if let scalar = UnicodeScalar(codePoint) { |
304 | 14.1k | out.append(scalar) |
305 | 14.1k | } else if codePoint < 0xD800 || codePoint >= 0xE000 { |
306 | 0 | // Not a valid Unicode scalar. |
307 | 0 | return nil |
308 | 605 | } else if codePoint >= 0xDC00 { |
309 | 15 | // Low surrogate without a preceding high surrogate. |
310 | 15 | return nil |
311 | 590 | } else { |
312 | 590 | // We have a high surrogate (in the range 0xD800..<0xDC00), so |
313 | 590 | // verify that it is followed by a low surrogate. |
314 | 590 | guard chars.next() == "\\", chars.next() == "u" else { |
315 | 57 | // High surrogate was not followed by a Unicode escape sequence. |
316 | 57 | return nil |
317 | 533 | } |
318 | 533 | if let digit1 = chars.next(), |
319 | 533 | let d1 = fromHexDigit(digit1), |
320 | 533 | let digit2 = chars.next(), |
321 | 533 | let d2 = fromHexDigit(digit2), |
322 | 533 | let digit3 = chars.next(), |
323 | 533 | let d3 = fromHexDigit(digit3), |
324 | 533 | let digit4 = chars.next(), |
325 | 533 | let d4 = fromHexDigit(digit4) |
326 | 533 | { |
327 | 507 | let follower = ((d1 * 16 + d2) * 16 + d3) * 16 + d4 |
328 | 507 | guard 0xDC00 <= follower && follower < 0xE000 else { |
329 | 26 | // High surrogate was not followed by a low surrogate. |
330 | 26 | return nil |
331 | 481 | } |
332 | 481 | let high = codePoint - 0xD800 |
333 | 481 | let low = follower - 0xDC00 |
334 | 481 | let composed = 0x10000 | high << 10 | low |
335 | 481 | guard let composedScalar = UnicodeScalar(composed) else { |
336 | 0 | // Composed value is not a valid Unicode scalar. |
337 | 0 | return nil |
338 | 481 | } |
339 | 481 | out.append(composedScalar) |
340 | 481 | } else { |
341 | 26 | // Malformed \u escape for low surrogate |
342 | 26 | return nil |
343 | 481 | } |
344 | 14.6k | } |
345 | 14.6k | } else { |
346 | 162 | // Malformed \u escape |
347 | 162 | return nil |
348 | 162 | } |
349 | 17.4k | case UInt32(asciiLowerB): // \b |
350 | 123 | out.append("\u{08}") |
351 | 17.4k | case UInt32(asciiLowerF): // \f |
352 | 110 | out.append("\u{0c}") |
353 | 17.4k | case UInt32(asciiLowerN): // \n |
354 | 135 | out.append("\u{0a}") |
355 | 17.4k | case UInt32(asciiLowerR): // \r |
356 | 178 | out.append("\u{0d}") |
357 | 17.4k | case UInt32(asciiLowerT): // \t |
358 | 241 | out.append("\u{09}") |
359 | 17.4k | case UInt32(asciiDoubleQuote), UInt32(asciiBackslash), |
360 | 1.65k | UInt32(asciiForwardSlash): // " \ / |
361 | 1.65k | out.append(escaped) |
362 | 17.4k | default: |
363 | 69 | return nil // Unrecognized escape |
364 | 17.4k | } |
365 | 17.1k | } else { |
366 | 0 | return nil // Input ends with backslash |
367 | 0 | } |
368 | 1.20M | default: |
369 | 1.19M | out.append(c) |
370 | 1.20M | } |
371 | 1.20M | } |
372 | 14.6k | return String(out) |
373 | 14.9k | } |
374 | | |
375 | | /// |
376 | | /// The basic scanner support is entirely private |
377 | | /// |
378 | | /// For performance, it works directly against UTF-8 bytes in memory. |
379 | | internal struct JSONScanner { |
380 | | private let source: UnsafeRawBufferPointer |
381 | | private var index: UnsafeRawBufferPointer.Index |
382 | 206k | private var numberParser = DoubleParser() |
383 | | internal let options: JSONDecodingOptions |
384 | | internal let extensions: any ExtensionMap |
385 | | internal var recursionBudget: Int |
386 | | |
387 | | /// True if the scanner has read all of the data from the source, with the |
388 | | /// exception of any trailing whitespace (which is consumed by reading this |
389 | | /// property). |
390 | | internal var complete: Bool { |
391 | 58.6k | mutating get { |
392 | 58.6k | skipWhitespace() |
393 | 58.6k | return !hasMoreContent |
394 | 58.6k | } |
395 | | } |
396 | | |
397 | | /// True if the scanner has not yet reached the end of the source. |
398 | 2.21G | private var hasMoreContent: Bool { |
399 | 2.21G | index != source.endIndex |
400 | 2.21G | } |
401 | | |
402 | | /// The byte (UTF-8 code unit) at the scanner's current position. |
403 | 2.75G | private var currentByte: UInt8 { |
404 | 2.75G | source[index] |
405 | 2.75G | } |
406 | | |
407 | | internal init( |
408 | | source: UnsafeRawBufferPointer, |
409 | | options: JSONDecodingOptions, |
410 | | extensions: (any ExtensionMap)? |
411 | 199k | ) { |
412 | 199k | self.source = source |
413 | 199k | self.index = source.startIndex |
414 | 199k | self.recursionBudget = options.messageDepthLimit |
415 | 199k | self.options = options |
416 | 199k | self.extensions = extensions ?? SimpleExtensionMap() |
417 | 199k | } |
418 | | |
419 | 4.91M | internal mutating func incrementRecursionDepth() throws { |
420 | 4.91M | recursionBudget -= 1 |
421 | 4.91M | if recursionBudget < 0 { |
422 | 225 | throw JSONDecodingError.messageDepthLimit |
423 | 4.91M | } |
424 | 4.91M | } |
425 | | |
426 | 6.03M | internal mutating func decrementRecursionDepth() { |
427 | 6.03M | recursionBudget += 1 |
428 | 6.03M | // This should never happen, if it does, something is probably corrupting memory, and |
429 | 6.03M | // simply throwing doesn't make much sense. |
430 | 6.03M | if recursionBudget > options.messageDepthLimit { |
431 | 0 | fatalError("Somehow JSONDecoding unwound more objects than it started") |
432 | 0 | } |
433 | 6.03M | } |
434 | | |
435 | | /// Advances the scanner to the next position in the source. |
436 | 306M | private mutating func advance() { |
437 | 306M | source.formIndex(after: &index) |
438 | 306M | } |
439 | | |
440 | | /// Skip whitespace. |
441 | 150M | private mutating func skipWhitespace() { |
442 | 204M | while hasMoreContent { |
443 | 204M | let u = currentByte |
444 | 204M | switch u { |
445 | 204M | case asciiSpace, asciiTab, asciiNewLine, asciiCarriageReturn: |
446 | 54.3M | advance() |
447 | 204M | default: |
448 | 150M | return |
449 | 204M | } |
450 | 54.3M | } |
451 | 218k | } |
452 | | |
453 | | /// Returns (but does not consume) the next non-whitespace |
454 | | /// character. This is used by google.protobuf.Value, for |
455 | | /// example, for custom JSON parsing. |
456 | 522k | internal mutating func peekOneCharacter() throws -> Character { |
457 | 522k | skipWhitespace() |
458 | 522k | guard hasMoreContent else { |
459 | 43 | throw JSONDecodingError.truncated |
460 | 522k | } |
461 | 522k | return Character(UnicodeScalar(UInt32(currentByte))!) |
462 | 522k | } |
463 | | |
464 | | // Parse the leading UInt64 from the provided utf8 bytes. |
465 | | // |
466 | | // This is called in three different situations: |
467 | | // |
468 | | // * Unquoted number. |
469 | | // |
470 | | // * Simple quoted number. If a number is quoted but has no |
471 | | // backslashes, the caller can use this directly on the UTF8 by |
472 | | // just verifying the quote marks. This code returns `nil` if it |
473 | | // sees a backslash, in which case the caller will need to handle ... |
474 | | // |
475 | | // * Complex quoted number. In this case, the caller must parse the |
476 | | // quoted value as a string, then convert the string to utf8 and |
477 | | // use this to parse the result. This is slow but fortunately |
478 | | // rare. |
479 | | // |
480 | | // In the common case where the number is written in integer form, |
481 | | // this code does a simple straight conversion. If the number is in |
482 | | // floating-point format, this uses a slower and less accurate |
483 | | // approach: it identifies a substring comprising a float, and then |
484 | | // uses Double() and UInt64() to convert that string to an unsigned |
485 | | // integer. In particular, it cannot preserve full 64-bit integer |
486 | | // values when they are written in floating-point format. |
487 | | // |
488 | | // If it encounters a "\" backslash character, it returns a nil. This |
489 | | // is used by callers that are parsing quoted numbers. See nextSInt() |
490 | | // and nextUInt() below. |
491 | | private func parseBareUInt64( |
492 | | source: UnsafeRawBufferPointer, |
493 | | index: inout UnsafeRawBufferPointer.Index, |
494 | | end: UnsafeRawBufferPointer.Index |
495 | 358k | ) throws -> UInt64? { |
496 | 358k | if index == end { |
497 | 22 | throw JSONDecodingError.truncated |
498 | 358k | } |
499 | 358k | let start = index |
500 | 358k | let c = source[index] |
501 | 358k | switch c { |
502 | 358k | case asciiZero: // 0 |
503 | 214k | source.formIndex(after: &index) |
504 | 214k | if index != end { |
505 | 213k | let after = source[index] |
506 | 213k | switch after { |
507 | 213k | case asciiZero...asciiNine: // 0...9 |
508 | 5 | // leading '0' forbidden unless it is the only digit |
509 | 5 | throw JSONDecodingError.leadingZero |
510 | 213k | case asciiPeriod, asciiLowerE, asciiUpperE: // . e |
511 | 1.78k | // Slow path: JSON numbers can be written in floating-point notation |
512 | 1.78k | index = start |
513 | 1.78k | if let d = try parseBareDouble( |
514 | 1.78k | source: source, |
515 | 1.78k | index: &index, |
516 | 1.78k | end: end |
517 | 1.78k | ) { |
518 | 1.75k | if let u = UInt64(exactly: d) { |
519 | 1.74k | return u |
520 | 1.74k | } |
521 | 4 | } |
522 | 4 | throw JSONDecodingError.malformedNumber |
523 | 213k | case asciiBackslash: |
524 | 812 | return nil |
525 | 213k | default: |
526 | 211k | return 0 |
527 | 213k | } |
528 | 213k | } |
529 | 251 | return 0 |
530 | 358k | case asciiOne...asciiNine: // 1...9 |
531 | 142k | var n = 0 as UInt64 |
532 | 357k | while index != end { |
533 | 355k | let digit = source[index] |
534 | 355k | switch digit { |
535 | 355k | case asciiZero...asciiNine: // 0...9 |
536 | 215k | let val = UInt64(digit - asciiZero) |
537 | 215k | if n > UInt64.max / 10 || n * 10 > UInt64.max - val { |
538 | 8 | throw JSONDecodingError.numberRange |
539 | 215k | } |
540 | 215k | source.formIndex(after: &index) |
541 | 215k | n = n * 10 + val |
542 | 355k | case asciiPeriod, asciiLowerE, asciiUpperE: // . e |
543 | 2.80k | // Slow path: JSON allows floating-point notation for integers |
544 | 2.80k | index = start |
545 | 2.80k | if let d = try parseBareDouble( |
546 | 2.80k | source: source, |
547 | 2.80k | index: &index, |
548 | 2.80k | end: end |
549 | 2.80k | ) { |
550 | 2.75k | if let u = UInt64(exactly: d) { |
551 | 2.74k | return u |
552 | 2.74k | } |
553 | 11 | } |
554 | 11 | throw JSONDecodingError.malformedNumber |
555 | 355k | case asciiBackslash: |
556 | 574 | return nil |
557 | 355k | default: |
558 | 137k | return n |
559 | 355k | } |
560 | 215k | } |
561 | 1.97k | return n |
562 | 358k | case asciiBackslash: |
563 | 1.03k | return nil |
564 | 358k | default: |
565 | 195 | throw JSONDecodingError.malformedNumber |
566 | 358k | } |
567 | 358k | } |
568 | | |
569 | | // Parse the leading Int64 from the provided utf8. |
570 | | // |
571 | | // This uses parseBareUInt64() to do the heavy lifting; |
572 | | // we just check for a leading minus and negate the result |
573 | | // as necessary. |
574 | | // |
575 | | // As with parseBareUInt64(), if it encounters a "\" backslash |
576 | | // character, it returns a nil. This allows callers to use this to |
577 | | // do a "fast-path" decode of simple quoted numbers by parsing the |
578 | | // UTF8 directly, only falling back to a full String decode when |
579 | | // absolutely necessary. |
580 | | private func parseBareSInt64( |
581 | | source: UnsafeRawBufferPointer, |
582 | | index: inout UnsafeRawBufferPointer.Index, |
583 | | end: UnsafeRawBufferPointer.Index |
584 | 240k | ) throws -> Int64? { |
585 | 240k | if index == end { |
586 | 40 | throw JSONDecodingError.truncated |
587 | 240k | } |
588 | 240k | let c = source[index] |
589 | 240k | if c == asciiMinus { // - |
590 | 15.4k | source.formIndex(after: &index) |
591 | 15.4k | if index == end { |
592 | 7 | throw JSONDecodingError.truncated |
593 | 15.3k | } |
594 | 15.3k | // character after '-' must be digit |
595 | 15.3k | let digit = source[index] |
596 | 15.3k | if digit < asciiZero || digit > asciiNine { |
597 | 13 | throw JSONDecodingError.malformedNumber |
598 | 15.3k | } |
599 | 15.3k | if let n = try parseBareUInt64(source: source, index: &index, end: end) { |
600 | 14.8k | let limit: UInt64 = 0x8000_0000_0000_0000 // -Int64.min |
601 | 14.8k | if n >= limit { |
602 | 309 | if n > limit { |
603 | 100 | // Too large negative number |
604 | 100 | throw JSONDecodingError.numberRange |
605 | 209 | } else { |
606 | 209 | return Int64.min // Special case for Int64.min |
607 | 209 | } |
608 | 14.5k | } |
609 | 14.5k | return -Int64(bitPattern: n) |
610 | 14.8k | } else { |
611 | 484 | return nil |
612 | 484 | } |
613 | 225k | } else if let n = try parseBareUInt64(source: source, index: &index, end: end) { |
614 | 223k | if n > UInt64(bitPattern: Int64.max) { |
615 | 104 | throw JSONDecodingError.numberRange |
616 | 223k | } |
617 | 223k | return Int64(bitPattern: n) |
618 | 223k | } else { |
619 | 1.24k | return nil |
620 | 1.24k | } |
621 | 240k | } |
622 | | |
623 | | // Identify a floating-point token in the upcoming UTF8 bytes. |
624 | | // |
625 | | // This implements the full grammar defined by the JSON RFC 7159. |
626 | | // Note that Swift's string-to-number conversions are much more |
627 | | // lenient, so this is necessary if we want to accurately reject |
628 | | // malformed JSON numbers. |
629 | | // |
630 | | // This is used by nextDouble() and nextFloat() to parse double and |
631 | | // floating-point values, including values that happen to be in quotes. |
632 | | // It's also used by the slow path in parseBareSInt64() and parseBareUInt64() |
633 | | // above to handle integer values that are written in float-point notation. |
634 | | private func parseBareDouble( |
635 | | source: UnsafeRawBufferPointer, |
636 | | index: inout UnsafeRawBufferPointer.Index, |
637 | | end: UnsafeRawBufferPointer.Index |
638 | 2.02M | ) throws -> Double? { |
639 | 2.02M | // RFC 7159 defines the grammar for JSON numbers as: |
640 | 2.02M | // number = [ minus ] int [ frac ] [ exp ] |
641 | 2.02M | if index == end { |
642 | 8 | throw JSONDecodingError.truncated |
643 | 2.02M | } |
644 | 2.02M | let start = index |
645 | 2.02M | var c = source[index] |
646 | 2.02M | if c == asciiBackslash { |
647 | 1.12k | return nil |
648 | 2.01M | } |
649 | 2.01M | |
650 | 2.01M | // Optional leading minus sign |
651 | 2.01M | if c == asciiMinus { // - |
652 | 8.58k | source.formIndex(after: &index) |
653 | 8.58k | if index == end { |
654 | 4 | index = start |
655 | 4 | throw JSONDecodingError.truncated |
656 | 8.58k | } |
657 | 8.58k | c = source[index] |
658 | 8.58k | if c == asciiBackslash { |
659 | 543 | return nil |
660 | 8.03k | } |
661 | 2.01M | } else if c == asciiUpperN { // Maybe NaN? |
662 | 1.41k | // Return nil, let the caller deal with it. |
663 | 1.41k | return nil |
664 | 2.01M | } |
665 | 2.01M | |
666 | 2.01M | if c == asciiUpperI { // Maybe Infinity, Inf, -Infinity, or -Inf ? |
667 | 3.23k | // Return nil, let the caller deal with it. |
668 | 3.23k | return nil |
669 | 2.01M | } |
670 | 2.01M | |
671 | 2.01M | // Integer part can be zero or a series of digits not starting with zero |
672 | 2.01M | // int = zero / (digit1-9 *DIGIT) |
673 | 2.01M | switch c { |
674 | 2.01M | case asciiZero: |
675 | 1.58M | // First digit can be zero only if not followed by a digit |
676 | 1.58M | source.formIndex(after: &index) |
677 | 1.58M | if index == end { |
678 | 537 | return 0.0 |
679 | 1.58M | } |
680 | 1.58M | c = source[index] |
681 | 1.58M | if c == asciiBackslash { |
682 | 181 | return nil |
683 | 1.58M | } |
684 | 1.58M | if c >= asciiZero && c <= asciiNine { |
685 | 9 | throw JSONDecodingError.leadingZero |
686 | 9 | } |
687 | 2.01M | case asciiOne...asciiNine: |
688 | 1.01M | while c >= asciiZero && c <= asciiNine { |
689 | 588k | source.formIndex(after: &index) |
690 | 588k | if index == end { |
691 | 1.18k | if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { |
692 | 1.18k | return d |
693 | 1.18k | } else { |
694 | 4 | throw JSONDecodingError.invalidUTF8 |
695 | 4 | } |
696 | 587k | } |
697 | 587k | c = source[index] |
698 | 587k | if c == asciiBackslash { |
699 | 327 | return nil |
700 | 587k | } |
701 | 587k | } |
702 | 2.01M | default: |
703 | 271 | // Integer part cannot be empty |
704 | 271 | throw JSONDecodingError.malformedNumber |
705 | 2.01M | } |
706 | 2.01M | |
707 | 2.01M | // frac = decimal-point 1*DIGIT |
708 | 2.01M | if c == asciiPeriod { |
709 | 15.7k | source.formIndex(after: &index) |
710 | 15.7k | if index == end { |
711 | 16 | // decimal point must have a following digit |
712 | 16 | throw JSONDecodingError.truncated |
713 | 15.7k | } |
714 | 15.7k | c = source[index] |
715 | 15.7k | switch c { |
716 | 15.7k | case asciiZero...asciiNine: // 0...9 |
717 | 1.08M | while c >= asciiZero && c <= asciiNine { |
718 | 1.07M | source.formIndex(after: &index) |
719 | 1.07M | if index == end { |
720 | 1.43k | if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { |
721 | 1.42k | return d |
722 | 1.42k | } else { |
723 | 4 | throw JSONDecodingError.invalidUTF8 |
724 | 4 | } |
725 | 1.07M | } |
726 | 1.07M | c = source[index] |
727 | 1.07M | if c == asciiBackslash { |
728 | 610 | return nil |
729 | 1.07M | } |
730 | 1.07M | } |
731 | 15.7k | case asciiBackslash: |
732 | 415 | return nil |
733 | 15.7k | default: |
734 | 22 | // decimal point must be followed by at least one digit |
735 | 22 | throw JSONDecodingError.malformedNumber |
736 | 15.7k | } |
737 | 2.00M | } |
738 | 2.00M | |
739 | 2.00M | // exp = e [ minus / plus ] 1*DIGIT |
740 | 2.00M | if c == asciiLowerE || c == asciiUpperE { |
741 | 20.8k | source.formIndex(after: &index) |
742 | 20.8k | if index == end { |
743 | 49 | // "e" must be followed by +,-, or digit |
744 | 49 | throw JSONDecodingError.truncated |
745 | 20.7k | } |
746 | 20.7k | c = source[index] |
747 | 20.7k | if c == asciiBackslash { |
748 | 247 | return nil |
749 | 20.5k | } |
750 | 20.5k | if c == asciiPlus || c == asciiMinus { // + - |
751 | 15.5k | source.formIndex(after: &index) |
752 | 15.5k | if index == end { |
753 | 15 | // must be at least one digit in exponent |
754 | 15 | throw JSONDecodingError.truncated |
755 | 15.5k | } |
756 | 15.5k | c = source[index] |
757 | 15.5k | if c == asciiBackslash { |
758 | 531 | return nil |
759 | 14.9k | } |
760 | 19.9k | } |
761 | 19.9k | switch c { |
762 | 19.9k | case asciiZero...asciiNine: |
763 | 945k | while c >= asciiZero && c <= asciiNine { |
764 | 941k | source.formIndex(after: &index) |
765 | 941k | if index == end { |
766 | 9.33k | if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { |
767 | 9.31k | return d |
768 | 9.31k | } else { |
769 | 21 | throw JSONDecodingError.invalidUTF8 |
770 | 21 | } |
771 | 932k | } |
772 | 932k | c = source[index] |
773 | 932k | if c == asciiBackslash { |
774 | 7.13k | return nil |
775 | 925k | } |
776 | 925k | } |
777 | 19.9k | default: |
778 | 45 | // must be at least one digit in exponent |
779 | 45 | throw JSONDecodingError.malformedNumber |
780 | 19.9k | } |
781 | 1.99M | } |
782 | 1.99M | if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { |
783 | 1.99M | return d |
784 | 1.99M | } else { |
785 | 28 | throw JSONDecodingError.invalidUTF8 |
786 | 28 | } |
787 | 1.99M | } |
788 | | |
789 | | /// Returns a fully-parsed string with all backslash escapes |
790 | | /// correctly processed, or nil if next token is not a string. |
791 | | /// |
792 | | /// Assumes the leading quote has been verified (but not consumed) |
793 | 53.0k | private mutating func parseOptionalQuotedString() -> String? { |
794 | 53.0k | // Caller has already asserted that currentByte == quote here |
795 | 53.0k | var sawBackslash = false |
796 | 53.0k | advance() |
797 | 53.0k | let start = index |
798 | 8.07M | while hasMoreContent { |
799 | 8.07M | switch currentByte { |
800 | 8.07M | case asciiDoubleQuote: // " |
801 | 45.7k | let s = utf8ToString(bytes: source, start: start, end: index) |
802 | 45.7k | advance() |
803 | 45.7k | if let t = s { |
804 | 45.7k | if sawBackslash { |
805 | 14.9k | return decodeString(t) |
806 | 30.7k | } else { |
807 | 30.7k | return t |
808 | 30.7k | } |
809 | 45.7k | } else { |
810 | 38 | return nil // Invalid UTF8 |
811 | 38 | } |
812 | 8.07M | case asciiBackslash: // \ |
813 | 19.1k | advance() |
814 | 19.1k | guard hasMoreContent else { |
815 | 32 | return nil // Unterminated escape |
816 | 19.0k | } |
817 | 19.0k | sawBackslash = true |
818 | 8.07M | case 0..<asciiSpace: |
819 | 7.13k | // Unescaped control characters (U+0000...U+001F) are not |
820 | 7.13k | // allowed inside a JSON string; they must be escaped. |
821 | 7.13k | return nil |
822 | 8.07M | default: |
823 | 8.00M | break |
824 | 8.07M | } |
825 | 8.02M | advance() |
826 | 8.02M | } |
827 | 77 | return nil // Unterminated quoted string |
828 | 53.0k | } |
829 | | |
830 | | /// Parse an unsigned integer, whether or not its quoted. |
831 | | /// This also handles cases such as quoted numbers that have |
832 | | /// backslash escapes in them. |
833 | | /// |
834 | | /// This supports the full range of UInt64 (whether quoted or not) |
835 | | /// unless the number is written in floating-point format. In that |
836 | | /// case, we decode it with only Double precision. |
837 | 106k | internal mutating func nextUInt() throws -> UInt64 { |
838 | 106k | skipWhitespace() |
839 | 106k | guard hasMoreContent else { |
840 | 40 | throw JSONDecodingError.truncated |
841 | 106k | } |
842 | 106k | let c = currentByte |
843 | 106k | if c == asciiDoubleQuote { |
844 | 12.2k | let start = index |
845 | 12.2k | advance() |
846 | 12.2k | if let u = try parseBareUInt64( |
847 | 12.2k | source: source, |
848 | 12.2k | index: &index, |
849 | 12.2k | end: source.endIndex |
850 | 12.2k | ) { |
851 | 11.6k | guard hasMoreContent else { |
852 | 3 | throw JSONDecodingError.truncated |
853 | 11.6k | } |
854 | 11.6k | if currentByte != asciiDoubleQuote { |
855 | 12 | throw JSONDecodingError.malformedNumber |
856 | 11.6k | } |
857 | 11.6k | advance() |
858 | 11.6k | return u |
859 | 11.6k | } else { |
860 | 577 | // Couldn't parse because it had a "\" in the string, |
861 | 577 | // so parse out the quoted string and then reparse |
862 | 577 | // the result to get a UInt. |
863 | 577 | index = start |
864 | 577 | let s = try nextQuotedString() |
865 | 553 | let raw = s.data(using: String.Encoding.utf8)! |
866 | 659 | let n = try raw.withUnsafeBytes { |
867 | 659 | (body: UnsafeRawBufferPointer) -> UInt64? in |
868 | 659 | if body.count > 0 { |
869 | 659 | var index = body.startIndex |
870 | 659 | let end = body.endIndex |
871 | 659 | if let u = try parseBareUInt64( |
872 | 659 | source: body, |
873 | 659 | index: &index, |
874 | 659 | end: end |
875 | 659 | ) { |
876 | 651 | if index == end { |
877 | 642 | return u |
878 | 642 | } |
879 | 14 | } |
880 | 14 | } |
881 | 14 | return nil |
882 | 659 | } |
883 | 550 | if let n = n { |
884 | 539 | return n |
885 | 539 | } |
886 | 11 | } |
887 | 11 | } else if let u = try parseBareUInt64( |
888 | 106k | source: source, |
889 | 106k | index: &index, |
890 | 106k | end: source.endIndex |
891 | 106k | ) { |
892 | 93.8k | return u |
893 | 93.8k | } |
894 | 14 | throw JSONDecodingError.malformedNumber |
895 | 106k | } |
896 | | |
897 | | /// Parse a signed integer, quoted or not, including handling |
898 | | /// backslash escapes for quoted values. |
899 | | /// |
900 | | /// This supports the full range of Int64 (whether quoted or not) |
901 | | /// unless the number is written in floating-point format. In that |
902 | | /// case, we decode it with only Double precision. |
903 | 215k | internal mutating func nextSInt() throws -> Int64 { |
904 | 215k | skipWhitespace() |
905 | 215k | guard hasMoreContent else { |
906 | 61 | throw JSONDecodingError.truncated |
907 | 215k | } |
908 | 215k | let c = currentByte |
909 | 215k | if c == asciiDoubleQuote { |
910 | 20.1k | let start = index |
911 | 20.1k | advance() |
912 | 20.1k | if let s = try parseBareSInt64( |
913 | 20.1k | source: source, |
914 | 20.1k | index: &index, |
915 | 20.1k | end: source.endIndex |
916 | 20.1k | ) { |
917 | 18.7k | guard hasMoreContent else { |
918 | 4 | throw JSONDecodingError.truncated |
919 | 18.7k | } |
920 | 18.7k | if currentByte != asciiDoubleQuote { |
921 | 6 | throw JSONDecodingError.malformedNumber |
922 | 18.7k | } |
923 | 18.7k | advance() |
924 | 18.7k | return s |
925 | 18.7k | } else { |
926 | 1.28k | // Couldn't parse because it had a "\" in the string, |
927 | 1.28k | // so parse out the quoted string and then reparse |
928 | 1.28k | // the result as an SInt. |
929 | 1.28k | index = start |
930 | 1.28k | let s = try nextQuotedString() |
931 | 1.24k | let raw = s.data(using: String.Encoding.utf8)! |
932 | 1.65k | let n = try raw.withUnsafeBytes { |
933 | 1.65k | (body: UnsafeRawBufferPointer) -> Int64? in |
934 | 1.65k | if body.count > 0 { |
935 | 1.65k | var index = body.startIndex |
936 | 1.65k | let end = body.endIndex |
937 | 1.65k | if let s = try parseBareSInt64( |
938 | 1.65k | source: body, |
939 | 1.65k | index: &index, |
940 | 1.65k | end: end |
941 | 1.65k | ) { |
942 | 1.61k | if index == end { |
943 | 1.58k | return s |
944 | 1.58k | } |
945 | 44 | } |
946 | 44 | } |
947 | 44 | return nil |
948 | 1.65k | } |
949 | 1.22k | if let n = n { |
950 | 1.18k | return n |
951 | 1.18k | } |
952 | 41 | } |
953 | 41 | } else if let s = try parseBareSInt64( |
954 | 215k | source: source, |
955 | 215k | index: &index, |
956 | 215k | end: source.endIndex |
957 | 215k | ) { |
958 | 194k | return s |
959 | 194k | } |
960 | 49 | throw JSONDecodingError.malformedNumber |
961 | 215k | } |
962 | | |
963 | | /// Parse the next Float value, regardless of whether it |
964 | | /// is quoted, including handling backslash escapes for |
965 | | /// quoted strings. |
966 | 1.45M | internal mutating func nextFloat() throws -> Float { |
967 | 1.45M | skipWhitespace() |
968 | 1.45M | guard hasMoreContent else { |
969 | 20 | throw JSONDecodingError.truncated |
970 | 1.45M | } |
971 | 1.45M | let c = currentByte |
972 | 1.45M | if c == asciiDoubleQuote { // " |
973 | 11.2k | let start = index |
974 | 11.2k | advance() |
975 | 11.2k | if let d = try parseBareDouble( |
976 | 11.2k | source: source, |
977 | 11.2k | index: &index, |
978 | 11.2k | end: source.endIndex |
979 | 11.2k | ) { |
980 | 1.42k | guard hasMoreContent else { |
981 | 1 | throw JSONDecodingError.truncated |
982 | 1.42k | } |
983 | 1.42k | if currentByte != asciiDoubleQuote { |
984 | 12 | throw JSONDecodingError.malformedNumber |
985 | 1.41k | } |
986 | 1.41k | advance() |
987 | 1.41k | // A value that parses as a finite Double can still be out of |
988 | 1.41k | // range for Float (for example "1e39"), in which case the |
989 | 1.41k | // conversion yields an infinity. Reject it, matching the |
990 | 1.41k | // unquoted path below. |
991 | 1.41k | let f = Float(d) |
992 | 1.41k | if f.isFinite { |
993 | 1.40k | return f |
994 | 1.40k | } |
995 | 1 | throw JSONDecodingError.malformedNumber |
996 | 9.84k | } else { |
997 | 9.84k | // Slow Path: parseBareDouble returned nil: It might be |
998 | 9.84k | // a valid float, but had something that |
999 | 9.84k | // parseBareDouble cannot directly handle. So we reset, |
1000 | 9.84k | // try a full string parse, then examine the result: |
1001 | 9.84k | index = start |
1002 | 9.84k | let s = try nextQuotedString() |
1003 | 9.69k | switch s { |
1004 | 9.69k | case "NaN": return Float.nan |
1005 | 9.69k | case "Inf": return Float.infinity |
1006 | 9.69k | case "-Inf": return -Float.infinity |
1007 | 9.69k | case "Infinity": return Float.infinity |
1008 | 9.69k | case "-Infinity": return -Float.infinity |
1009 | 9.69k | default: |
1010 | 8.79k | let raw = s.data(using: String.Encoding.utf8)! |
1011 | 9.82k | let n = try raw.withUnsafeBytes { |
1012 | 9.82k | (body: UnsafeRawBufferPointer) -> Float? in |
1013 | 9.82k | if body.count > 0 { |
1014 | 9.82k | var index = body.startIndex |
1015 | 9.82k | let end = body.endIndex |
1016 | 9.82k | if let d = try parseBareDouble( |
1017 | 9.82k | source: body, |
1018 | 9.82k | index: &index, |
1019 | 9.82k | end: end |
1020 | 9.82k | ) { |
1021 | 9.59k | let f = Float(d) |
1022 | 9.59k | if index == end && f.isFinite { |
1023 | 9.56k | return f |
1024 | 9.56k | } |
1025 | 229 | } |
1026 | 229 | } |
1027 | 229 | return nil |
1028 | 9.82k | } |
1029 | 8.76k | if let n = n { |
1030 | 8.55k | return n |
1031 | 8.55k | } |
1032 | 9.69k | } |
1033 | 207 | } |
1034 | 1.44M | } else { |
1035 | 1.44M | if let d = try parseBareDouble( |
1036 | 1.44M | source: source, |
1037 | 1.44M | index: &index, |
1038 | 1.44M | end: source.endIndex |
1039 | 1.44M | ) { |
1040 | 1.44M | let f = Float(d) |
1041 | 1.44M | if f.isFinite { |
1042 | 1.44M | return f |
1043 | 1.44M | } |
1044 | 9 | } |
1045 | 216 | } |
1046 | 216 | throw JSONDecodingError.malformedNumber |
1047 | 1.45M | } |
1048 | | |
1049 | | /// Parse the next Double value, regardless of whether it |
1050 | | /// is quoted, including handling backslash escapes for |
1051 | | /// quoted strings. |
1052 | 530k | internal mutating func nextDouble() throws -> Double { |
1053 | 530k | skipWhitespace() |
1054 | 530k | guard hasMoreContent else { |
1055 | 21 | throw JSONDecodingError.truncated |
1056 | 530k | } |
1057 | 530k | let c = currentByte |
1058 | 530k | if c == asciiDoubleQuote { // " |
1059 | 3.48k | let start = index |
1060 | 3.48k | advance() |
1061 | 3.48k | if let d = try parseBareDouble( |
1062 | 3.48k | source: source, |
1063 | 3.48k | index: &index, |
1064 | 3.48k | end: source.endIndex |
1065 | 3.48k | ) { |
1066 | 412 | guard hasMoreContent else { |
1067 | 2 | throw JSONDecodingError.truncated |
1068 | 410 | } |
1069 | 410 | if currentByte != asciiDoubleQuote { |
1070 | 11 | throw JSONDecodingError.malformedNumber |
1071 | 399 | } |
1072 | 399 | advance() |
1073 | 399 | return d |
1074 | 3.05k | } else { |
1075 | 3.05k | // Slow Path: parseBareDouble returned nil: It might be |
1076 | 3.05k | // a valid float, but had something that |
1077 | 3.05k | // parseBareDouble cannot directly handle. So we reset, |
1078 | 3.05k | // try a full string parse, then examine the result: |
1079 | 3.05k | index = start |
1080 | 3.05k | let s = try nextQuotedString() |
1081 | 2.90k | switch s { |
1082 | 2.90k | case "NaN": return Double.nan |
1083 | 2.90k | case "Inf": return Double.infinity |
1084 | 2.90k | case "-Inf": return -Double.infinity |
1085 | 2.90k | case "Infinity": return Double.infinity |
1086 | 2.90k | case "-Infinity": return -Double.infinity |
1087 | 2.90k | default: |
1088 | 1.03k | let raw = s.data(using: String.Encoding.utf8)! |
1089 | 1.40k | let n = try raw.withUnsafeBytes { |
1090 | 1.40k | (body: UnsafeRawBufferPointer) -> Double? in |
1091 | 1.40k | if body.count > 0 { |
1092 | 1.40k | var index = body.startIndex |
1093 | 1.40k | let end = body.endIndex |
1094 | 1.40k | if let d = try parseBareDouble( |
1095 | 1.40k | source: body, |
1096 | 1.40k | index: &index, |
1097 | 1.40k | end: end |
1098 | 1.40k | ) { |
1099 | 1.21k | if index == end { |
1100 | 1.19k | return d |
1101 | 1.19k | } |
1102 | 180 | } |
1103 | 180 | } |
1104 | 180 | return nil |
1105 | 1.40k | } |
1106 | 1.01k | if let n = n { |
1107 | 838 | return n |
1108 | 838 | } |
1109 | 2.90k | } |
1110 | 175 | } |
1111 | 526k | } else { |
1112 | 526k | if let d = try parseBareDouble( |
1113 | 526k | source: source, |
1114 | 526k | index: &index, |
1115 | 526k | end: source.endIndex |
1116 | 526k | ) { |
1117 | 526k | return d |
1118 | 526k | } |
1119 | 193 | } |
1120 | 193 | throw JSONDecodingError.malformedNumber |
1121 | 530k | } |
1122 | | |
1123 | | /// Return the contents of the following quoted string, |
1124 | | /// or throw an error if the next token is not a string. |
1125 | 359k | internal mutating func nextQuotedString() throws -> String { |
1126 | 359k | skipWhitespace() |
1127 | 359k | guard hasMoreContent else { |
1128 | 486 | throw JSONDecodingError.truncated |
1129 | 359k | } |
1130 | 359k | let c = currentByte |
1131 | 359k | if c != asciiDoubleQuote { |
1132 | 2.82k | throw JSONDecodingError.malformedString |
1133 | 356k | } |
1134 | 356k | if let s = parseOptionalQuotedString() { |
1135 | 296k | return s |
1136 | 296k | } else { |
1137 | 59.6k | throw JSONDecodingError.malformedString |
1138 | 59.6k | } |
1139 | 356k | } |
1140 | | |
1141 | | /// Return the contents of the following quoted string, |
1142 | | /// or nil if the next token is not a string. |
1143 | | /// This will only throw an error if the next token starts |
1144 | | /// out as a string but is malformed in some way. |
1145 | 0 | internal mutating func nextOptionalQuotedString() throws -> String? { |
1146 | 0 | skipWhitespace() |
1147 | 0 | guard hasMoreContent else { |
1148 | 0 | return nil |
1149 | 0 | } |
1150 | 0 | let c = currentByte |
1151 | 0 | if c != asciiDoubleQuote { |
1152 | 0 | return nil |
1153 | 0 | } |
1154 | 0 | return try nextQuotedString() |
1155 | 0 | } |
1156 | | |
1157 | | /// Return a Data with the decoded contents of the |
1158 | | /// following base-64 string. |
1159 | | /// |
1160 | | /// Notes on Google's implementation: |
1161 | | /// * Google's C++ implementation accepts arbitrary whitespace |
1162 | | /// mixed in with the base-64 characters |
1163 | | /// * Google's C++ implementation ignores missing '=' characters |
1164 | | /// but if present, there must be the exact correct number of them. |
1165 | | /// * Google's C++ implementation accepts both "regular" and |
1166 | | /// "web-safe" base-64 variants (it seems to prefer the |
1167 | | /// web-safe version as defined in RFC 4648 |
1168 | 32.5k | internal mutating func nextBytesValue() throws -> Data { |
1169 | 32.5k | skipWhitespace() |
1170 | 32.5k | guard hasMoreContent else { |
1171 | 84 | throw JSONDecodingError.truncated |
1172 | 32.4k | } |
1173 | 32.4k | return try parseBytes(source: source, index: &index, end: source.endIndex) |
1174 | 32.5k | } |
1175 | | |
1176 | | /// Private function to help parse keywords. |
1177 | 39.4k | private mutating func skipOptionalKeyword(bytes: [UInt8]) -> Bool { |
1178 | 39.4k | let start = index |
1179 | 160k | for b in bytes { |
1180 | 160k | guard hasMoreContent else { |
1181 | 101 | index = start |
1182 | 101 | return false |
1183 | 160k | } |
1184 | 160k | let c = currentByte |
1185 | 160k | if c != b { |
1186 | 98 | index = start |
1187 | 98 | return false |
1188 | 160k | } |
1189 | 160k | advance() |
1190 | 160k | } |
1191 | 39.2k | if hasMoreContent { |
1192 | 38.7k | let c = currentByte |
1193 | 38.7k | if (c >= asciiUpperA && c <= asciiUpperZ) || (c >= asciiLowerA && c <= asciiLowerZ) { |
1194 | 21 | index = start |
1195 | 21 | return false |
1196 | 38.7k | } |
1197 | 39.2k | } |
1198 | 39.2k | return true |
1199 | 39.4k | } |
1200 | | |
1201 | | /// If the next token is the identifier "null", consume it and return true. |
1202 | 1.31M | internal mutating func skipOptionalNull() -> Bool { |
1203 | 1.31M | skipWhitespace() |
1204 | 1.45M | if hasMoreContent && currentByte == asciiLowerN { |
1205 | 50.7k | return skipOptionalKeyword(bytes: [ |
1206 | 50.7k | asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL, |
1207 | 50.7k | ]) |
1208 | 1.26M | } |
1209 | 1.26M | return false |
1210 | 1.31M | } |
1211 | | |
1212 | | /// Return the following Bool "true" or "false", including |
1213 | | /// full processing of quoted boolean values. (Used in map |
1214 | | /// keys, for instance.) |
1215 | 7.92k | internal mutating func nextBool() throws -> Bool { |
1216 | 7.92k | skipWhitespace() |
1217 | 7.92k | guard hasMoreContent else { |
1218 | 25 | throw JSONDecodingError.truncated |
1219 | 7.90k | } |
1220 | 7.90k | let c = currentByte |
1221 | 7.90k | switch c { |
1222 | 7.90k | case asciiLowerF: // f |
1223 | 1.33k | if skipOptionalKeyword(bytes: [ |
1224 | 1.33k | asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE, |
1225 | 1.33k | ]) { |
1226 | 1.33k | return false |
1227 | 1.33k | } |
1228 | 7.90k | case asciiLowerT: // t |
1229 | 6.53k | if skipOptionalKeyword(bytes: [ |
1230 | 6.53k | asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE, |
1231 | 6.53k | ]) { |
1232 | 6.52k | return true |
1233 | 6.52k | } |
1234 | 7.90k | default: |
1235 | 30 | break |
1236 | 7.90k | } |
1237 | 51 | throw JSONDecodingError.malformedBool |
1238 | 7.92k | } |
1239 | | |
1240 | | /// Return the following Bool "true" or "false", including |
1241 | | /// full processing of quoted boolean values. (Used in map |
1242 | | /// keys, for instance.) |
1243 | 1.33k | internal mutating func nextQuotedBool() throws -> Bool { |
1244 | 1.33k | skipWhitespace() |
1245 | 1.33k | guard hasMoreContent else { |
1246 | 0 | throw JSONDecodingError.truncated |
1247 | 1.33k | } |
1248 | 1.33k | if currentByte != asciiDoubleQuote { |
1249 | 0 | throw JSONDecodingError.unquotedMapKey |
1250 | 1.33k | } |
1251 | 1.33k | if let s = parseOptionalQuotedString() { |
1252 | 1.21k | switch s { |
1253 | 1.21k | case "false": return false |
1254 | 1.21k | case "true": return true |
1255 | 1.21k | default: break |
1256 | 1.21k | } |
1257 | 189 | } |
1258 | 189 | throw JSONDecodingError.malformedBool |
1259 | 1.33k | } |
1260 | | |
1261 | | /// Returns pointer/count spanning the UTF8 bytes of the next regular |
1262 | | /// key or nil if the key contains a backslash (and therefore requires |
1263 | | /// the full string-parsing logic to properly parse). |
1264 | 262k | private mutating func nextOptionalKey() throws -> UnsafeRawBufferPointer? { |
1265 | 262k | skipWhitespace() |
1266 | 262k | let stringStart = index |
1267 | 262k | guard hasMoreContent else { |
1268 | 246 | throw JSONDecodingError.truncated |
1269 | 261k | } |
1270 | 261k | if currentByte != asciiDoubleQuote { |
1271 | 356 | return nil |
1272 | 261k | } |
1273 | 261k | advance() |
1274 | 261k | let nameStart = index |
1275 | 5.81M | while hasMoreContent && currentByte != asciiDoubleQuote { |
1276 | 5.55M | if currentByte == asciiBackslash || currentByte < asciiSpace { |
1277 | 4.96k | // Backslash escapes go through the slow path; unescaped control |
1278 | 4.96k | // characters are invalid there and get rejected uniformly. |
1279 | 4.96k | index = stringStart // Reset to open quote |
1280 | 4.96k | return nil |
1281 | 5.55M | } |
1282 | 5.55M | advance() |
1283 | 5.55M | } |
1284 | 256k | guard hasMoreContent else { |
1285 | 432 | throw JSONDecodingError.truncated |
1286 | 256k | } |
1287 | 256k | let buff = UnsafeRawBufferPointer( |
1288 | 256k | start: source.baseAddress! + nameStart, |
1289 | 256k | count: index - nameStart |
1290 | 256k | ) |
1291 | 256k | advance() |
1292 | 256k | return buff |
1293 | 262k | } |
1294 | | |
1295 | | /// Parse a field name, look it up in the provided field name map, |
1296 | | /// and return the corresponding field number. |
1297 | | /// |
1298 | | /// Throws if field name cannot be parsed. |
1299 | | /// If it encounters an unknown field name, it throws |
1300 | | /// unless `options.ignoreUnknownFields` is set, in which case |
1301 | | /// it silently skips it. |
1302 | | internal mutating func nextFieldNumber( |
1303 | | names: _NameMap, |
1304 | | messageType: any Message.Type |
1305 | 172k | ) throws -> Int? { |
1306 | 191k | while true { |
1307 | 191k | var fieldName: String |
1308 | 191k | if let key = try nextOptionalKey() { |
1309 | 187k | // Fast path: We parsed it as UTF8 bytes... |
1310 | 187k | try skipRequiredCharacter(asciiColon) // : |
1311 | 187k | if let fieldNumber = names.number(forJSONName: key) { |
1312 | 97.9k | return fieldNumber |
1313 | 97.9k | } |
1314 | 89.3k | if let s = utf8ToString(bytes: key.baseAddress!, count: key.count) { |
1315 | 89.2k | fieldName = s |
1316 | 89.2k | } else { |
1317 | 49 | throw JSONDecodingError.invalidUTF8 |
1318 | 89.2k | } |
1319 | 89.2k | } else { |
1320 | 3.69k | // Slow path: We parsed a String; lookups from String are slower. |
1321 | 3.69k | fieldName = try nextQuotedString() |
1322 | 369 | try skipRequiredCharacter(asciiColon) // : |
1323 | 203 | if let fieldNumber = names.number(forJSONName: fieldName) { |
1324 | 54 | return fieldNumber |
1325 | 149 | } |
1326 | 89.4k | } |
1327 | 89.4k | if let first = fieldName.utf8.first, first == UInt8(ascii: "["), |
1328 | 89.4k | let last = fieldName.utf8.last, last == UInt8(ascii: "]") |
1329 | 89.4k | { |
1330 | 42.2k | fieldName.removeFirst() |
1331 | 42.2k | fieldName.removeLast() |
1332 | 42.2k | if let fieldNumber = extensions.fieldNumberForProto(messageType: messageType, protoFieldName: fieldName) |
1333 | 42.2k | { |
1334 | 32.7k | return fieldNumber |
1335 | 32.7k | } |
1336 | 56.6k | } |
1337 | 56.6k | if !options.ignoreUnknownFields { |
1338 | 214 | throw JSONDecodingError.unknownField(fieldName) |
1339 | 56.3k | } |
1340 | 56.3k | // Unknown field, skip it and try to parse the next field name |
1341 | 56.3k | try skipValue() |
1342 | 55.9k | if skipOptionalObjectEnd() { |
1343 | 37.3k | return nil |
1344 | 37.3k | } |
1345 | 18.6k | try skipRequiredComma() |
1346 | 18.5k | } |
1347 | 0 | } |
1348 | | |
1349 | | /// Parse the next token as a string or numeric enum value. Throws |
1350 | | /// unrecognizedEnumValue if the string/number can't initialize the |
1351 | | /// enum. Will throw other errors if the JSON is malformed. |
1352 | 11.5k | internal mutating func nextEnumValue<E: Enum>() throws -> E? { |
1353 | 11.5k | func throwOrIgnore() throws -> E? { |
1354 | 3.76k | if options.ignoreUnknownFields { |
1355 | 3.53k | return nil |
1356 | 3.53k | } else { |
1357 | 223 | throw JSONDecodingError.unrecognizedEnumValue |
1358 | 223 | } |
1359 | 3.76k | } |
1360 | 11.5k | skipWhitespace() |
1361 | 11.5k | guard hasMoreContent else { |
1362 | 18 | throw JSONDecodingError.truncated |
1363 | 11.5k | } |
1364 | 11.5k | if currentByte == asciiDoubleQuote { |
1365 | 432 | if let name = try nextOptionalKey() { |
1366 | 64 | if let e = E(rawUTF8: name) { |
1367 | 0 | return e |
1368 | 64 | } else { |
1369 | 64 | return try throwOrIgnore() |
1370 | 64 | } |
1371 | 364 | } |
1372 | 364 | let name = try nextQuotedString() |
1373 | 360 | if let e = E(name: name) { |
1374 | 0 | return e |
1375 | 360 | } else { |
1376 | 360 | return try throwOrIgnore() |
1377 | 360 | } |
1378 | 11.1k | } else { |
1379 | 11.1k | let n = try nextSInt() |
1380 | 10.9k | if let i = Int(exactly: n) { |
1381 | 10.9k | if let e = E(rawValue: i) { |
1382 | 8.86k | return e |
1383 | 8.86k | } else { |
1384 | 2.04k | return try throwOrIgnore() |
1385 | 2.04k | } |
1386 | 10.9k | } else { |
1387 | 0 | throw JSONDecodingError.numberRange |
1388 | 0 | } |
1389 | 10.9k | } |
1390 | 11.5k | } |
1391 | | |
1392 | | /// Helper for skipping a single-character token. |
1393 | 11.6M | private mutating func skipRequiredCharacter(_ required: UInt8) throws { |
1394 | 11.6M | skipWhitespace() |
1395 | 11.6M | guard hasMoreContent else { |
1396 | 13.2k | throw JSONDecodingError.truncated |
1397 | 11.6M | } |
1398 | 11.6M | let next = currentByte |
1399 | 11.6M | if next == required { |
1400 | 11.6M | advance() |
1401 | 11.6M | return |
1402 | 11.6M | } |
1403 | 5.47k | throw JSONDecodingError.failure |
1404 | 11.6M | } |
1405 | | |
1406 | | /// Skip "{", throw if that's not the next character. |
1407 | 3.11M | internal mutating func skipRequiredObjectStart() throws { |
1408 | 3.11M | try skipRequiredCharacter(asciiOpenCurlyBracket) // { |
1409 | 3.09M | try incrementRecursionDepth() |
1410 | 3.09M | } |
1411 | | |
1412 | | /// Skip ",", throw if that's not the next character. |
1413 | 85.8M | internal mutating func skipRequiredComma() throws { |
1414 | 85.8M | try skipRequiredCharacter(asciiComma) |
1415 | 85.7M | } |
1416 | | |
1417 | | /// Skip ":", throw if that's not the next character. |
1418 | 767k | internal mutating func skipRequiredColon() throws { |
1419 | 767k | try skipRequiredCharacter(asciiColon) |
1420 | 766k | } |
1421 | | |
1422 | | /// Skip "[", throw if that's not the next character. |
1423 | 144k | internal mutating func skipRequiredArrayStart() throws { |
1424 | 144k | try skipRequiredCharacter(asciiOpenSquareBracket) // [ |
1425 | 143k | } |
1426 | | |
1427 | | /// Helper for skipping optional single-character tokens. |
1428 | 75.2M | private mutating func skipOptionalCharacter(_ c: UInt8) -> Bool { |
1429 | 75.2M | skipWhitespace() |
1430 | 75.2M | if hasMoreContent && currentByte == c { |
1431 | 5.71M | advance() |
1432 | 5.71M | return true |
1433 | 69.4M | } |
1434 | 69.4M | return false |
1435 | 75.2M | } |
1436 | | |
1437 | | /// If the next non-whitespace character is "[", skip it |
1438 | | /// and return true. Otherwise, return false. |
1439 | 91.6k | internal mutating func skipOptionalArrayStart() -> Bool { |
1440 | 91.6k | skipOptionalCharacter(asciiOpenSquareBracket) |
1441 | 91.6k | } |
1442 | | |
1443 | | /// If the next non-whitespace character is "]", skip it |
1444 | | /// and return true. Otherwise, return false. |
1445 | 21.3M | internal mutating func skipOptionalArrayEnd() -> Bool { |
1446 | 21.3M | skipOptionalCharacter(asciiCloseSquareBracket) // ]// ] |
1447 | 21.3M | } |
1448 | | |
1449 | | /// If the next non-whitespace character is "}", skip it |
1450 | | /// and return true. Otherwise, return false. |
1451 | 13.2M | internal mutating func skipOptionalObjectEnd() -> Bool { |
1452 | 13.2M | let result = skipOptionalCharacter(asciiCloseCurlyBracket) // } |
1453 | 13.2M | if result { |
1454 | 4.14M | decrementRecursionDepth() |
1455 | 4.14M | } |
1456 | 13.2M | return result |
1457 | 13.2M | } |
1458 | | |
1459 | | /// Return the next complete JSON structure as a string. |
1460 | | /// For example, this might return "true", or "123.456", |
1461 | | /// or "{\"foo\": 7, \"bar\": [8, 9]}" |
1462 | | /// |
1463 | | /// Used by Any to get the upcoming JSON value as a string. |
1464 | | /// Note: The value might be an object or array. |
1465 | 862 | internal mutating func skip() throws -> String { |
1466 | 862 | skipWhitespace() |
1467 | 862 | let start = index |
1468 | 862 | try skipValue() |
1469 | 729 | if let s = utf8ToString(bytes: source, start: start, end: index) { |
1470 | 726 | return s |
1471 | 726 | } else { |
1472 | 3 | throw JSONDecodingError.invalidUTF8 |
1473 | 3 | } |
1474 | 729 | } |
1475 | | |
1476 | | /// Advance index past the next value. This is used |
1477 | | /// by skip() and by unknown field handling. |
1478 | | /// Note: This handles objects {...} recursively but arrays [...] non-recursively |
1479 | | /// This avoids us requiring excessive stack space for deeply nested |
1480 | | /// arrays (which are not included in the recursion budget check). |
1481 | 83.0k | private mutating func skipValue() throws { |
1482 | 83.0k | skipWhitespace() |
1483 | 83.0k | var totalArrayDepth = 0 |
1484 | 92.2k | while true { |
1485 | 92.2k | var arrayDepth = 0 |
1486 | 119k | while skipOptionalArrayStart() { |
1487 | 27.0k | arrayDepth += 1 |
1488 | 92.2k | } |
1489 | 92.2k | guard hasMoreContent else { |
1490 | 241 | throw JSONDecodingError.truncated |
1491 | 91.9k | } |
1492 | 91.9k | switch currentByte { |
1493 | 91.9k | case asciiDoubleQuote: // " begins a string |
1494 | 20.6k | try skipString() |
1495 | 91.9k | case asciiOpenCurlyBracket: // { begins an object |
1496 | 21.6k | try skipObject() |
1497 | 91.9k | case asciiCloseSquareBracket: // ] ends an empty array |
1498 | 2.73k | if arrayDepth == 0 { |
1499 | 8 | throw JSONDecodingError.failure |
1500 | 2.73k | } |
1501 | 2.73k | // We also close out [[]] or [[[]]] here |
1502 | 5.84k | while arrayDepth > 0 && skipOptionalArrayEnd() { |
1503 | 3.11k | arrayDepth -= 1 |
1504 | 3.11k | } |
1505 | 91.9k | case asciiLowerN: // n must be null |
1506 | 8.18k | if !skipOptionalKeyword(bytes: [ |
1507 | 8.18k | asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL, |
1508 | 8.18k | ]) { |
1509 | 22 | throw JSONDecodingError.truncated |
1510 | 22 | } |
1511 | 91.9k | case asciiLowerF: // f must be false |
1512 | 1.96k | if !skipOptionalKeyword(bytes: [ |
1513 | 1.96k | asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE, |
1514 | 1.96k | ]) { |
1515 | 12 | throw JSONDecodingError.truncated |
1516 | 12 | } |
1517 | 91.9k | case asciiLowerT: // t must be true |
1518 | 1.05k | if !skipOptionalKeyword(bytes: [ |
1519 | 1.05k | asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE, |
1520 | 1.05k | ]) { |
1521 | 13 | throw JSONDecodingError.truncated |
1522 | 13 | } |
1523 | 91.9k | default: // everything else is a number token |
1524 | 35.7k | _ = try nextDouble() |
1525 | 91.9k | } |
1526 | 91.2k | totalArrayDepth += arrayDepth |
1527 | 105k | while totalArrayDepth > 0 && skipOptionalArrayEnd() { |
1528 | 13.9k | totalArrayDepth -= 1 |
1529 | 91.2k | } |
1530 | 91.2k | if totalArrayDepth > 0 { |
1531 | 9.33k | try skipRequiredComma() |
1532 | 81.9k | } else { |
1533 | 81.9k | return |
1534 | 81.9k | } |
1535 | 9.19k | } |
1536 | 0 | } |
1537 | | |
1538 | | /// Advance the index past the next complete {...} construct. |
1539 | 21.6k | private mutating func skipObject() throws { |
1540 | 21.6k | try skipRequiredObjectStart() |
1541 | 21.6k | if skipOptionalObjectEnd() { |
1542 | 10.0k | return |
1543 | 11.5k | } |
1544 | 14.5k | while true { |
1545 | 14.5k | skipWhitespace() |
1546 | 14.5k | try skipString() |
1547 | 14.4k | try skipRequiredColon() |
1548 | 14.4k | try skipValue() |
1549 | 14.1k | if skipOptionalObjectEnd() { |
1550 | 11.2k | return |
1551 | 11.2k | } |
1552 | 2.97k | try skipRequiredComma() |
1553 | 2.97k | } |
1554 | 0 | } |
1555 | | |
1556 | | /// Advance the index past the next complete quoted string. |
1557 | | /// |
1558 | | // Caveat: This does not fully validate; it will accept |
1559 | | // strings that have malformed \ escapes. |
1560 | | // |
1561 | | // It would be nice to do better, but I don't think it's critical, |
1562 | | // since there are many reasons that strings (and other tokens for |
1563 | | // that matter) may be skippable but not parsable. For example: |
1564 | | // Old clients that don't know new field types will skip fields |
1565 | | // they don't know; newer clients may reject the same input due to |
1566 | | // schema mismatches or other issues. |
1567 | 35.1k | private mutating func skipString() throws { |
1568 | 35.1k | guard hasMoreContent else { |
1569 | 33 | throw JSONDecodingError.truncated |
1570 | 35.1k | } |
1571 | 35.1k | if currentByte != asciiDoubleQuote { |
1572 | 25 | throw JSONDecodingError.malformedString |
1573 | 35.1k | } |
1574 | 35.1k | advance() |
1575 | 1.04M | while hasMoreContent { |
1576 | 1.04M | let c = currentByte |
1577 | 1.04M | switch c { |
1578 | 1.04M | case asciiDoubleQuote: |
1579 | 35.0k | advance() |
1580 | 35.0k | return |
1581 | 1.04M | case asciiBackslash: |
1582 | 342 | advance() |
1583 | 342 | guard hasMoreContent else { |
1584 | 3 | throw JSONDecodingError.truncated |
1585 | 339 | } |
1586 | 339 | advance() |
1587 | 1.04M | default: |
1588 | 1.00M | advance() |
1589 | 1.04M | } |
1590 | 1.00M | } |
1591 | 56 | throw JSONDecodingError.truncated |
1592 | 35.1k | } |
1593 | | } |