@@ -408,36 +408,61 @@ private enum _JSONStringParser {
408
408
}
409
409
}
410
410
411
- static func decodeStringWithEscapes( source: UnsafeBufferPointer < UInt8 > ) -> String {
411
+ /// Helper iterator decoding UTF8 sequence to UnicodeScalar stream.
412
+ struct ScalarIterator < S: Sequence > : IteratorProtocol where S. Element == UInt8 {
413
+ var backing : S . Iterator
414
+ var decoder : UTF8
415
+ init ( _ source: S ) {
416
+ self . backing = source. makeIterator ( )
417
+ self . decoder = UTF8 ( )
418
+ }
419
+ mutating func next( ) -> UnicodeScalar ? {
420
+ switch decoder. decode ( & backing) {
421
+ case . scalarValue( let scalar) : return scalar
422
+ case . emptyInput: return nil
423
+ case . error: fatalError ( " invalid " )
424
+ }
425
+ }
426
+ }
427
+
428
+ static func decodeStringWithEscapes( source: UnsafeBufferPointer < UInt8 > ) -> String ? {
412
429
var string : String = " "
413
- var iter = source. makeIterator ( )
414
- var utf8Decoder = UTF8 ( )
415
- DECODE: while true {
416
- switch utf8Decoder. decode ( & iter) {
417
- case . scalarValue( let scalar) :
418
- if scalar == " \\ " {
419
- switch iter. next ( ) {
420
- case UInt8 ( ascii: " \" " ) : string. append ( " \" " )
421
- case UInt8 ( ascii: " ' " ) : string. append ( " ' " )
422
- case UInt8 ( ascii: " \\ " ) : string. append ( " \\ " )
423
- case UInt8 ( ascii: " / " ) : string. append ( " / " )
424
- case UInt8 ( ascii: " b " ) : string. append ( " \u{08} " ) // \b
425
- case UInt8 ( ascii: " f " ) : string. append ( " \u{0C} " ) // \f
426
- case UInt8 ( ascii: " n " ) : string. append ( " \u{0A} " ) // \n
427
- case UInt8 ( ascii: " r " ) : string. append ( " \u{0D} " ) // \r
428
- case UInt8 ( ascii: " t " ) : string. append ( " \u{09} " ) // \t
429
- case UInt8 ( ascii: " u " ) :
430
- fatalError ( " unimplemented " )
431
- default :
432
- fatalError ( " invalid " )
430
+ var iter = ScalarIterator ( source)
431
+ while let scalar = iter. next ( ) {
432
+ // NOTE: We don't report detailed errors because we only care well-formed
433
+ // payloads from the compiler.
434
+ if scalar == " \\ " {
435
+ switch iter. next ( ) {
436
+ case " \" " : string. append ( " \" " )
437
+ case " ' " : string. append ( " ' " )
438
+ case " \\ " : string. append ( " \\ " )
439
+ case " / " : string. append ( " / " )
440
+ case " b " : string. append ( " \u{08} " )
441
+ case " f " : string. append ( " \u{0C} " )
442
+ case " n " : string. append ( " \u{0A} " )
443
+ case " r " : string. append ( " \u{0D} " )
444
+ case " t " : string. append ( " \u{09} " )
445
+ case " u " :
446
+ // We don't care performance of this because \uFFFF style escape is
447
+ // pretty rare. We only do it for control characters.
448
+ let buffer : [ UInt8 ] = [ iter. next ( ) , iter. next ( ) , iter. next ( ) , iter. next ( ) ]
449
+ . compactMap { $0 }
450
+ . compactMap { UInt8 ( exactly: $0. value) }
451
+
452
+ guard
453
+ buffer. count == 4 ,
454
+ let result: UInt16 = buffer. withUnsafeBufferPointer ( _JSONNumberParser. parseHexIntegerDigits ( source: ) ) ,
455
+ let scalar = UnicodeScalar ( result)
456
+ else {
457
+ return nil
433
458
}
434
- } else {
435
459
string. append ( Character ( scalar) )
460
+ default :
461
+ // invalid escape sequence
462
+ return nil
436
463
}
437
- case . emptyInput:
438
- break DECODE
439
- case . error:
440
- fatalError ( " invalid " )
464
+ } else {
465
+ string. append ( Character ( scalar) )
441
466
}
442
467
}
443
468
return string
@@ -488,6 +513,34 @@ private enum _JSONNumberParser {
488
513
}
489
514
return value
490
515
}
516
+
517
+ static func parseHexIntegerDigits< Integer: FixedWidthInteger > ( source: UnsafeBufferPointer < UInt8 > ) -> Integer ? {
518
+ var source = source [ ... ]
519
+ var value : Integer = 0
520
+ var overflowed : Bool = false
521
+ while let digit = source. popFirst ( ) {
522
+ let digitValue : Integer
523
+ switch digit {
524
+ case UInt8 ( ascii: " 0 " ) ... UInt8 ( ascii: " 9 " ) :
525
+ digitValue = Integer ( truncatingIfNeeded: digit &- UInt8 ( ascii: " 0 " ) )
526
+ case UInt8 ( ascii: " a " ) ... UInt8 ( ascii: " f " ) :
527
+ digitValue = Integer ( truncatingIfNeeded: digit &- UInt8 ( ascii: " a " ) &+ 10 )
528
+ case UInt8 ( ascii: " A " ) ... UInt8 ( ascii: " F " ) :
529
+ digitValue = Integer ( truncatingIfNeeded: digit &- UInt8 ( ascii: " A " ) &+ 10 )
530
+ default :
531
+ return nil
532
+ }
533
+ ( value, overflowed) = value. multipliedReportingOverflow ( by: 16 )
534
+ guard !overflowed else {
535
+ return nil
536
+ }
537
+ ( value, overflowed) = value. addingReportingOverflow ( digitValue)
538
+ guard !overflowed else {
539
+ return nil
540
+ }
541
+ }
542
+ return value
543
+ }
491
544
}
492
545
493
546
extension JSONMapValue {
0 commit comments