mirror of
https://github.com/apple/swift-foundation.git
synced 2025-05-23 22:13:35 +08:00
687 lines
23 KiB
Swift
687 lines
23 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if canImport(Darwin)
|
|
import Darwin
|
|
#elseif canImport(Glibc)
|
|
import Glibc
|
|
#endif
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Coding Path Node
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// This construction allows overall fewer and smaller allocations as the coding path is modified.
|
|
internal enum _CodingPathNode : Sendable {
|
|
case root
|
|
indirect case node(CodingKey, _CodingPathNode, depth: Int)
|
|
indirect case indexNode(Int, _CodingPathNode, depth: Int)
|
|
|
|
var path : [CodingKey] {
|
|
switch self {
|
|
case .root:
|
|
return []
|
|
case let .node(key, parent, _):
|
|
return parent.path + [key]
|
|
case let .indexNode(index, parent, _):
|
|
return parent.path + [_CodingKey(index: index)]
|
|
}
|
|
}
|
|
|
|
@inline(__always)
|
|
var depth: Int {
|
|
switch self {
|
|
case .root: return 0
|
|
case .node(_, _, let depth), .indexNode(_, _, let depth): return depth
|
|
}
|
|
}
|
|
|
|
@inline(__always)
|
|
func appending(_ key: __owned (some CodingKey)?) -> _CodingPathNode {
|
|
if let key {
|
|
return .node(key, self, depth: self.depth + 1)
|
|
} else {
|
|
return self
|
|
}
|
|
}
|
|
|
|
@inline(__always)
|
|
func path(byAppending key: __owned (some CodingKey)?) -> [CodingKey] {
|
|
if let key {
|
|
return self.path + [key]
|
|
}
|
|
return self.path
|
|
}
|
|
|
|
// Specializations for indexes, commonly used by unkeyed containers.
|
|
@inline(__always)
|
|
func appending(index: __owned Int) -> _CodingPathNode {
|
|
.indexNode(index, self, depth: self.depth + 1)
|
|
}
|
|
|
|
func path(byAppendingIndex index: __owned Int) -> [CodingKey] {
|
|
self.path + [_CodingKey(index: index)]
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Shared Key Type
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
internal enum _CodingKey : CodingKey {
|
|
case string(String)
|
|
case int(Int)
|
|
case index(Int)
|
|
case both(String, Int)
|
|
|
|
@inline(__always)
|
|
public init?(stringValue: String) {
|
|
self = .string(stringValue)
|
|
}
|
|
|
|
@inline(__always)
|
|
public init?(intValue: Int) {
|
|
self = .int(intValue)
|
|
}
|
|
|
|
@inline(__always)
|
|
internal init(index: Int) {
|
|
self = .index(index)
|
|
}
|
|
|
|
@inline(__always)
|
|
init(stringValue: String, intValue: Int?) {
|
|
if let intValue {
|
|
self = .both(stringValue, intValue)
|
|
} else {
|
|
self = .string(stringValue)
|
|
}
|
|
}
|
|
|
|
var stringValue: String {
|
|
switch self {
|
|
case let .string(str): return str
|
|
case let .int(int): return "\(int)"
|
|
case let .index(index): return "Index \(index)"
|
|
case let .both(str, _): return str
|
|
}
|
|
}
|
|
|
|
var intValue: Int? {
|
|
switch self {
|
|
case .string: return nil
|
|
case let .int(int): return int
|
|
case let .index(index): return index
|
|
case let .both(_, int): return int
|
|
}
|
|
}
|
|
|
|
internal static let `super` = _CodingKey.string("super")
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Character reading conveniences
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
extension UInt8 {
|
|
|
|
internal static var _space: UInt8 { UInt8(ascii: " ") }
|
|
internal static var _return: UInt8 { UInt8(ascii: "\r") }
|
|
internal static var _newline: UInt8 { UInt8(ascii: "\n") }
|
|
internal static var _tab: UInt8 { UInt8(ascii: "\t") }
|
|
|
|
internal static var _colon: UInt8 { UInt8(ascii: ":") }
|
|
internal static let _semicolon = UInt8(ascii: ";")
|
|
internal static var _comma: UInt8 { UInt8(ascii: ",") }
|
|
|
|
internal static var _openbrace: UInt8 { UInt8(ascii: "{") }
|
|
internal static var _closebrace: UInt8 { UInt8(ascii: "}") }
|
|
|
|
internal static var _openbracket: UInt8 { UInt8(ascii: "[") }
|
|
internal static var _closebracket: UInt8 { UInt8(ascii: "]") }
|
|
|
|
internal static let _openangle = UInt8(ascii: "<")
|
|
internal static let _closeangle = UInt8(ascii: ">")
|
|
|
|
internal static var _quote: UInt8 { UInt8(ascii: "\"") }
|
|
internal static var _backslash: UInt8 { UInt8(ascii: "\\") }
|
|
internal static var _forwardslash: UInt8 { UInt8(ascii: "/") }
|
|
|
|
internal static var _equal: UInt8 { UInt8(ascii: "=") }
|
|
internal static var _minus: UInt8 { UInt8(ascii: "-") }
|
|
internal static var _plus: UInt8 { UInt8(ascii: "+") }
|
|
internal static var _question: UInt8 { UInt8(ascii: "?") }
|
|
internal static var _exclamation: UInt8 { UInt8(ascii: "!") }
|
|
internal static var _ampersand: UInt8 { UInt8(ascii: "&") }
|
|
internal static var _pipe: UInt8 { UInt8(ascii: "|") }
|
|
internal static var _period: UInt8 { UInt8(ascii: ".") }
|
|
internal static var _e: UInt8 { UInt8(ascii: "e") }
|
|
internal static var _E: UInt8 { UInt8(ascii: "E") }
|
|
|
|
internal var digitValue: Int? {
|
|
guard _asciiNumbers.contains(self) else { return nil }
|
|
return Int(self &- UInt8(ascii: "0"))
|
|
}
|
|
|
|
internal var isLetter: Bool? {
|
|
return (0x41 ... 0x5a) ~= self || (0x61 ... 0x7a) ~= self
|
|
}
|
|
}
|
|
|
|
|
|
internal var _asciiNumbers: ClosedRange<UInt8> { UInt8(ascii: "0") ... UInt8(ascii: "9") }
|
|
internal var _hexCharsUpper: ClosedRange<UInt8> { UInt8(ascii: "A") ... UInt8(ascii: "F") }
|
|
internal var _hexCharsLower: ClosedRange<UInt8> { UInt8(ascii: "a") ... UInt8(ascii: "f") }
|
|
internal var _allLettersUpper: ClosedRange<UInt8> { UInt8(ascii: "A") ... UInt8(ascii: "Z") }
|
|
internal var _allLettersLower: ClosedRange<UInt8> { UInt8(ascii: "a") ... UInt8(ascii: "z") }
|
|
|
|
extension UInt8 {
|
|
internal var hexDigitValue: UInt8? {
|
|
switch self {
|
|
case _asciiNumbers:
|
|
return self - _asciiNumbers.lowerBound
|
|
case _hexCharsUpper:
|
|
// uppercase letters
|
|
return self - _hexCharsUpper.lowerBound &+ 10
|
|
case _hexCharsLower:
|
|
// lowercase letters
|
|
return self - _hexCharsLower.lowerBound &+ 10
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
internal var isValidHexDigit: Bool {
|
|
switch self {
|
|
case _asciiNumbers, _hexCharsUpper, _hexCharsLower:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Date parsing conveniences
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// A narrow reproduction of deprecated CFGregorianDate functionality for property list serialization usage.
|
|
internal extension Date {
|
|
static func isLeapYear(_ year: Int64) -> Bool {
|
|
var y = (year + 1) % 400
|
|
if y < 0 { y = -y }
|
|
return 0 == (y & 3) && y != 100 && y != 200 && y != 300
|
|
}
|
|
|
|
static func daysInYear(_ year: Int64) -> Int16 {
|
|
let DAYS_PER_YEAR = Int16(365)
|
|
return DAYS_PER_YEAR &+ (isLeapYear(year) ? 1 : 0)
|
|
}
|
|
|
|
static func daysBeforeMonth(_ month: Int8, year: Int64) -> Int16? {
|
|
switch month {
|
|
case 1: return 0
|
|
case 2: return 31
|
|
case 3: return 59 + (isLeapYear(year) ? 1 : 0)
|
|
case 4: return 90 + (isLeapYear(year) ? 1 : 0)
|
|
case 5: return 120 + (isLeapYear(year) ? 1 : 0)
|
|
case 6: return 151 + (isLeapYear(year) ? 1 : 0)
|
|
case 7: return 181 + (isLeapYear(year) ? 1 : 0)
|
|
case 8: return 212 + (isLeapYear(year) ? 1 : 0)
|
|
case 9: return 243 + (isLeapYear(year) ? 1 : 0)
|
|
case 10: return 273 + (isLeapYear(year) ? 1 : 0)
|
|
case 11: return 304 + (isLeapYear(year) ? 1 : 0)
|
|
case 12: return 334 + (isLeapYear(year) ? 1 : 0)
|
|
case 13: return 365 + (isLeapYear(year) ? 1 : 0) // Days before the end of December
|
|
default: return nil
|
|
}
|
|
}
|
|
|
|
static func daysAfterMonth(_ month: Int8, year: Int64) -> Int16 {
|
|
switch month {
|
|
case 0: return 365 + (isLeapYear(year) ? 1 : 0)
|
|
case 1: return 334 + (isLeapYear(year) ? 1 : 0)
|
|
case 2: return 306
|
|
case 3: return 275
|
|
case 4: return 245
|
|
case 5: return 214
|
|
case 6: return 184
|
|
case 7: return 153
|
|
case 8: return 122
|
|
case 9: return 92
|
|
case 10: return 61
|
|
case 11: return 31
|
|
default: return 0
|
|
}
|
|
}
|
|
|
|
/* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
|
|
static func daysSinceReferenceDate(year: Int64, month: Int8, day: Int8) -> Double {
|
|
let DAYS_PER_400_YEARS = 146097.0
|
|
let num400YearChunks = year / 400 // take care of as many multiples of 400 years as possible
|
|
var result = Double(num400YearChunks) * DAYS_PER_400_YEARS
|
|
let remainingYears = year - num400YearChunks &* 400
|
|
if remainingYears < 0 {
|
|
for curYear in remainingYears ..< 0 {
|
|
result -= Double(daysInYear(curYear))
|
|
}
|
|
} else {
|
|
for curYear in 0 ..< remainingYears {
|
|
result += Double(daysInYear(curYear))
|
|
}
|
|
}
|
|
if let daysBeforeMonth = daysBeforeMonth(month, year: year) {
|
|
result += Double(daysBeforeMonth)
|
|
} // else, an invalid month was previded and the result is just undefined.
|
|
result += Double(day - 1)
|
|
return result
|
|
}
|
|
|
|
init(gregorianYear year: Int64, month: Int8, day: Int8, hour: Int8, minute: Int8, second: Double) {
|
|
let SECONDS_IN_DAY = 86400.0
|
|
let REFERENCE_YEAR = Int64(2001)
|
|
var timeInterval = SECONDS_IN_DAY * Self.daysSinceReferenceDate(year: year - REFERENCE_YEAR, month: month, day: day)
|
|
|
|
let SECONDS_PER_HOUR = 3600.0
|
|
let SECONDS_PER_MINUTE = 60.0
|
|
timeInterval += Double(hour) * SECONDS_PER_HOUR + Double(minute) * SECONDS_PER_MINUTE + Double(second)
|
|
|
|
// No time zone considerations necessary here.
|
|
|
|
self = Date(timeIntervalSinceReferenceDate: timeInterval)
|
|
}
|
|
|
|
// year is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0
|
|
private static func gregorianYMD(from absolute: Int64) -> (year: Int64, month: Int8, day: Int8) {
|
|
var absolute = absolute
|
|
let b = absolute / 146097 // take care of as many multiples of 400 years as possible
|
|
var y = b * 400
|
|
|
|
var ydays: Int64
|
|
absolute -= b * 146097
|
|
while absolute < 0 {
|
|
y -= 1
|
|
absolute += Int64(daysAfterMonth(0, year: y))
|
|
}
|
|
|
|
// Now absolute is non-negative days to add to year
|
|
ydays = Int64(daysAfterMonth(0, year: y))
|
|
while ydays <= absolute {
|
|
y += 1
|
|
absolute -= ydays
|
|
ydays = Int64(daysAfterMonth(0, year: y))
|
|
}
|
|
|
|
// Now we have year and days-into-year
|
|
var m = Int8(absolute / 33 + 1) //search from the approximation
|
|
|
|
// Calculations above should guarantee that 0 <= absolute < 365, meaning 1 <= m <= 12. However, m+1 may well become out of bounds.
|
|
while let dbm = daysBeforeMonth(m + 1, year: y), dbm < absolute {
|
|
m &+= 1
|
|
}
|
|
|
|
let d = Int8(absolute - Int64(daysBeforeMonth(m, year: y)!) + 1)
|
|
|
|
return (y, m, d)
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Integer parsing conveniences
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
internal
|
|
func _parseIntegerDigits<Result: FixedWidthInteger>(
|
|
_ codeUnits: BufferView<UInt8>, isNegative: Bool
|
|
) -> Result? {
|
|
guard _fastPath(!codeUnits.isEmpty) else { return nil }
|
|
|
|
// ASCII constants, named for clarity:
|
|
let _0 = 48 as UInt8
|
|
|
|
let numericalUpperBound: UInt8 = _0 &+ 10
|
|
let multiplicand: Result = 10
|
|
var result: Result = 0
|
|
|
|
var iter = codeUnits.makeIterator()
|
|
while let digit = iter.next() {
|
|
let digitValue: Result
|
|
if _fastPath(digit >= _0 && digit < numericalUpperBound) {
|
|
digitValue = Result(truncatingIfNeeded: digit &- _0)
|
|
} else {
|
|
return nil
|
|
}
|
|
let overflow1: Bool
|
|
(result, overflow1) = result.multipliedReportingOverflow(by: multiplicand)
|
|
let overflow2: Bool
|
|
(result, overflow2) = isNegative
|
|
? result.subtractingReportingOverflow(digitValue)
|
|
: result.addingReportingOverflow(digitValue)
|
|
guard _fastPath(!overflow1 && !overflow2) else { return nil }
|
|
}
|
|
return result
|
|
}
|
|
|
|
internal
|
|
func _parseHexIntegerDigits<Result: FixedWidthInteger>(
|
|
_ codeUnits: BufferView<UInt8>, isNegative: Bool
|
|
) -> Result? {
|
|
guard _fastPath(!codeUnits.isEmpty) else { return nil }
|
|
|
|
// ASCII constants, named for clarity:
|
|
let _0 = 48 as UInt8, _A = 65 as UInt8, _a = 97 as UInt8
|
|
|
|
let numericalUpperBound = _0 &+ 10
|
|
let uppercaseUpperBound = _A &+ 6
|
|
let lowercaseUpperBound = _a &+ 6
|
|
let multiplicand: Result = 16
|
|
|
|
var result = 0 as Result
|
|
for digit in codeUnits {
|
|
let digitValue: Result
|
|
if _fastPath(digit >= _0 && digit < numericalUpperBound) {
|
|
digitValue = Result(truncatingIfNeeded: digit &- _0)
|
|
} else if _fastPath(digit >= _A && digit < uppercaseUpperBound) {
|
|
digitValue = Result(truncatingIfNeeded: digit &- _A &+ 10)
|
|
} else if _fastPath(digit >= _a && digit < lowercaseUpperBound) {
|
|
digitValue = Result(truncatingIfNeeded: digit &- _a &+ 10)
|
|
} else {
|
|
return nil
|
|
}
|
|
|
|
let overflow1: Bool
|
|
(result, overflow1) = result.multipliedReportingOverflow(by: multiplicand)
|
|
let overflow2: Bool
|
|
(result, overflow2) = isNegative
|
|
? result.subtractingReportingOverflow(digitValue)
|
|
: result.addingReportingOverflow(digitValue)
|
|
guard _fastPath(!overflow1 && !overflow2) else { return nil }
|
|
}
|
|
return result
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Error handling conveniences
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
internal
|
|
extension DecodingError {
|
|
static func _dataCorrupted(_ debugDescription: String, for node: _CodingPathNode, _ additionalKey: (some CodingKey)?) -> Self {
|
|
Self.dataCorrupted(.init(codingPath: node.path(byAppending: additionalKey), debugDescription: debugDescription))
|
|
}
|
|
|
|
static func _dataCorrupted(_ debugDescription: String, for node: _CodingPathNode) -> Self {
|
|
Self.dataCorrupted(.init(codingPath: node.path, debugDescription: debugDescription))
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Shared Plist Null Representation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Since plists do not support null values by default, we will encode them as "$null".
|
|
internal let _plistNull: StaticString = "$null"
|
|
internal let _plistNullString: String = String("$null")
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Plist Decoding Storage
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
internal struct _PlistDecodingStorage<T> {
|
|
// MARK: Properties
|
|
|
|
/// The container stack.
|
|
internal var containers: [T] = []
|
|
|
|
// MARK: - Initialization
|
|
|
|
/// Initializes `self` with no containers.
|
|
internal init() {}
|
|
|
|
// MARK: - Modifying the Stack
|
|
|
|
internal var count: Int {
|
|
return self.containers.count
|
|
}
|
|
|
|
internal var topContainer: T {
|
|
precondition(!self.containers.isEmpty, "Empty container stack.")
|
|
return self.containers.last.unsafelyUnwrapped
|
|
}
|
|
|
|
internal mutating func push(container: __owned T) {
|
|
self.containers.append(container)
|
|
}
|
|
|
|
internal mutating func popContainer() {
|
|
precondition(!self.containers.isEmpty, "Empty container stack.")
|
|
self.containers.removeLast()
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Buffer Reader
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct BufferReader {
|
|
let fullBuffer: BufferView<UInt8>
|
|
let startIndex: BufferViewIndex<UInt8>
|
|
var readIndex: BufferViewIndex<UInt8>
|
|
let endIndex: BufferViewIndex<UInt8>
|
|
|
|
@inline(__always)
|
|
init(bytes: BufferView<UInt8>) {
|
|
self.fullBuffer = bytes
|
|
self.startIndex = bytes.startIndex
|
|
self.readIndex = bytes.startIndex
|
|
self.endIndex = bytes.endIndex
|
|
}
|
|
|
|
@inline(__always)
|
|
init(bytes: BufferView<UInt8>, fullSource: BufferView<UInt8>) {
|
|
self.fullBuffer = fullSource
|
|
self.startIndex = bytes.startIndex
|
|
self.readIndex = bytes.startIndex
|
|
self.endIndex = bytes.endIndex
|
|
}
|
|
|
|
@inline(__always)
|
|
var isAtEnd : Bool {
|
|
readIndex == endIndex
|
|
}
|
|
|
|
@inline(__always)
|
|
func hasBytes(_ num: Int) -> Bool {
|
|
readIndex.advanced(by: num) <= endIndex
|
|
}
|
|
|
|
@inline(__always)
|
|
func index(offset: Int) -> BufferViewIndex<UInt8> {
|
|
readIndex.advanced(by: offset)
|
|
}
|
|
|
|
@inline(__always)
|
|
func byteOffset(at index: BufferViewIndex<UInt8>) -> Int {
|
|
fullBuffer.distance(from: fullBuffer.startIndex, to: index)
|
|
}
|
|
|
|
@inline(__always)
|
|
mutating func read() -> UInt8? {
|
|
guard !isAtEnd else {
|
|
return nil
|
|
}
|
|
|
|
defer { fullBuffer.formIndex(after: &readIndex) }
|
|
return fullBuffer[unchecked: readIndex]
|
|
}
|
|
|
|
@inline(__always)
|
|
func peek() -> UInt8? {
|
|
hasBytes(1) ? remainingBuffer[unchecked: readIndex] : nil
|
|
}
|
|
|
|
@_disfavoredOverload
|
|
@inline(__always)
|
|
func peek() -> (UInt8, UInt8)? {
|
|
let buf = remainingBuffer
|
|
return hasBytes(2) ? (buf[uncheckedOffset: 0],
|
|
buf[uncheckedOffset: 1]) : nil
|
|
}
|
|
|
|
@_disfavoredOverload
|
|
@inline(__always)
|
|
func peek() -> (UInt8, UInt8, UInt8)? {
|
|
let buf = remainingBuffer
|
|
return hasBytes(3) ? (buf[uncheckedOffset: 0],
|
|
buf[uncheckedOffset: 1],
|
|
buf[uncheckedOffset: 2]) : nil
|
|
}
|
|
|
|
@inline(__always)
|
|
func char(at index: BufferViewIndex<UInt8>) -> UInt8 {
|
|
return fullBuffer[index]
|
|
}
|
|
|
|
@inline(__always)
|
|
mutating func advance(_ amt: Int = 1) {
|
|
advance(&readIndex, by: amt)
|
|
}
|
|
|
|
@inline(__always)
|
|
func advance(_ idx: inout BufferViewIndex<UInt8>, by amt: Int = 1) {
|
|
fullBuffer.formIndex(&idx, offsetBy: amt)
|
|
}
|
|
|
|
@inline(__always)
|
|
func string(at dataIdx: BufferViewIndex<UInt8>, matches ptr: UnsafePointer<UInt8>, length: Int) -> Bool {
|
|
fullBuffer[dataIdx...].withUnsafeRawPointer { bufPtr, _ in
|
|
memcmp(bufPtr, ptr, length) == 0
|
|
}
|
|
}
|
|
|
|
@inline(__always)
|
|
func string(at dataidx: BufferViewIndex<UInt8>, matches str: StaticString) -> Bool {
|
|
string(at: dataidx, matches: str.utf8Start, length: str.utf8CodeUnitCount)
|
|
}
|
|
|
|
@inline(__always)
|
|
var bytes : BufferView<UInt8> {
|
|
fullBuffer[startIndex..<self.endIndex]
|
|
}
|
|
|
|
@inline(__always)
|
|
var remainingBuffer : BufferView<UInt8> {
|
|
fullBuffer[self.readIndex..<self.endIndex]
|
|
}
|
|
|
|
var lineNumber : Int {
|
|
assert(readIndex <= endIndex)
|
|
|
|
var count = 1
|
|
var p = startIndex
|
|
while p < readIndex {
|
|
if fullBuffer[unchecked: p] == ._return {
|
|
count += 1
|
|
let nextIndex = p.advanced(by: 1)
|
|
if nextIndex < readIndex && fullBuffer[unchecked: nextIndex] == ._newline {
|
|
p = nextIndex
|
|
}
|
|
} else if fullBuffer[offset: 1] == ._newline {
|
|
count += 1
|
|
}
|
|
fullBuffer.formIndex(&p, offsetBy: 1)
|
|
}
|
|
return count
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// UTF-8 Decoding
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// These UTF-8 decoding functions are cribbed and specialized from the stdlib.
|
|
|
|
@inline(__always)
|
|
private func _utf8ScalarLength(_ x: UInt8) -> Int? {
|
|
guard !UTF8.isContinuation(x) else { return nil }
|
|
if UTF8.isASCII(x) { return 1 }
|
|
return (~x).leadingZeroBitCount
|
|
}
|
|
|
|
@inline(__always)
|
|
private func _continuationPayload(_ x: UInt8) -> UInt32 {
|
|
return UInt32(x & 0x3F)
|
|
}
|
|
|
|
@inline(__always)
|
|
private func _decodeUTF8(_ x: UInt8) -> Unicode.Scalar? {
|
|
guard UTF8.isASCII(x) else { return nil }
|
|
return Unicode.Scalar(x)
|
|
}
|
|
|
|
@inline(__always)
|
|
private func _decodeUTF8(_ x: UInt8, _ y: UInt8) -> Unicode.Scalar? {
|
|
assert(_utf8ScalarLength(x) == 2)
|
|
guard UTF8.isContinuation(y) else { return nil }
|
|
let x = UInt32(x)
|
|
let value = ((x & 0b0001_1111) &<< 6) | _continuationPayload(y)
|
|
return Unicode.Scalar(value).unsafelyUnwrapped
|
|
}
|
|
|
|
@inline(__always)
|
|
private func _decodeUTF8(
|
|
_ x: UInt8, _ y: UInt8, _ z: UInt8
|
|
) -> Unicode.Scalar? {
|
|
assert(_utf8ScalarLength(x) == 3)
|
|
guard UTF8.isContinuation(y), UTF8.isContinuation(z) else { return nil }
|
|
let x = UInt32(x)
|
|
let value = ((x & 0b0000_1111) &<< 12)
|
|
| (_continuationPayload(y) &<< 6)
|
|
| _continuationPayload(z)
|
|
return Unicode.Scalar(value).unsafelyUnwrapped
|
|
}
|
|
|
|
@inline(__always)
|
|
private func _decodeUTF8(
|
|
_ x: UInt8, _ y: UInt8, _ z: UInt8, _ w: UInt8
|
|
) -> Unicode.Scalar? {
|
|
assert(_utf8ScalarLength(x) == 4)
|
|
guard UTF8.isContinuation(y), UTF8.isContinuation(z), UTF8.isContinuation(w) else { return nil }
|
|
let x = UInt32(x)
|
|
let value = ((x & 0b0000_1111) &<< 18)
|
|
| (_continuationPayload(y) &<< 12)
|
|
| (_continuationPayload(z) &<< 6)
|
|
| _continuationPayload(w)
|
|
return Unicode.Scalar(value).unsafelyUnwrapped
|
|
}
|
|
|
|
extension BufferView where Element == UInt8 {
|
|
internal func _decodeScalar() -> (Unicode.Scalar?, scalarLength: Int) {
|
|
let cu0 = self[uncheckedOffset: 0]
|
|
guard let len = _utf8ScalarLength(cu0), self.count >= len else { return (nil, 0) }
|
|
switch len {
|
|
case 1:
|
|
return (_decodeUTF8(cu0), len)
|
|
case 2:
|
|
return (_decodeUTF8(cu0, self[uncheckedOffset: 1]), len)
|
|
case 3:
|
|
return (_decodeUTF8(cu0, self[uncheckedOffset: 1], self[uncheckedOffset: 2]), len)
|
|
case 4:
|
|
return (_decodeUTF8(cu0, self[uncheckedOffset: 1], self[uncheckedOffset: 2], self[uncheckedOffset: 3]), len)
|
|
default:
|
|
fatalError()
|
|
}
|
|
}
|
|
}
|
|
|