blob: 50bd6df3b3c9f0c29518ccc913f02ed3a3826c0d [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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
//
//===----------------------------------------------------------------------===//
extension String {
/// Constructs a `String` in `resultStorage` containing the given UTF-8.
///
/// Low-level construction interface used by introspection
/// implementation in the runtime library.
@inlinable
@_silgen_name("swift_stringFromUTF8InRawMemory")
public // COMPILER_INTRINSIC
static func _fromUTF8InRawMemory(
_ resultStorage: UnsafeMutablePointer<String>,
start: UnsafeMutablePointer<UTF8.CodeUnit>,
utf8CodeUnitCount: Int
) {
resultStorage.initialize(to:
String._fromWellFormedUTF8(
UnsafeBufferPointer(start: start, count: utf8CodeUnitCount)))
}
@usableFromInline
static func _fromUTF8(
_ input: UnsafeBufferPointer<UInt8>, repair: Bool
) -> String? {
if _isAllASCII(input) {
return _fromASCII(input)
}
return _fromNonASCIIUTF8(input, repair: repair)
}
@usableFromInline
static func _fromASCII(_ input: UnsafeBufferPointer<UInt8>) -> String {
if let smol = _SmallUTF8String(input) {
return String(_StringGuts(smol))
}
let storage = _SwiftStringStorage<UInt8>.create(
capacity: input.count, count: input.count)
_sanityCheck(storage.count == input.count)
storage.start.initialize(
from: input.baseAddress._unsafelyUnwrappedUnchecked, count: input.count)
return String(_StringGuts(_large: storage))
}
@usableFromInline
static func _fromWellFormedUTF8(
_ input: UnsafeBufferPointer<UInt8>, repair: Bool = false
) -> String {
return String._fromUTF8(input, repair: repair)!
}
@inlinable
static func _fromWellFormedUTF16CodeUnits<C : RandomAccessCollection>(
_ input: C, repair: Bool = false
) -> String where C.Element == UTF16.CodeUnit {
if let smol = _SmallUTF8String(input) {
return String(_StringGuts(smol))
}
return String._fromCodeUnits(
input, encoding: UTF16.self, repairIllFormedSequences: repair)!
}
@inlinable
internal static func _fromCodeUnits<
Input: Collection, Encoding: Unicode.Encoding
>(
_ input: Input, encoding: Encoding.Type, repairIllFormedSequences: Bool
) -> String?
where Input.Element == Encoding.CodeUnit {
// TODO(SSO): small check
// Determine how many UTF-16 code units we'll need
let inputStream = input.makeIterator()
guard let (utf16Count, isASCII) = UTF16.transcodedLength(
of: inputStream,
decodedAs: encoding,
repairingIllFormedSequences: repairIllFormedSequences) else {
return nil
}
let capacity = utf16Count
if isASCII {
if let small = _SmallUTF8String(
_fromCodeUnits: input,
utf16Length: utf16Count,
isASCII: true,
Encoding.self
) {
return String(_StringGuts(small))
}
let storage = _SwiftStringStorage<UInt8>.create(
capacity: capacity,
count: utf16Count)
var p = storage.start
let sink: (UTF32.CodeUnit) -> Void = {
p.pointee = UTF8.CodeUnit($0)
p += 1
}
let hadError = transcode(
input.makeIterator(),
from: encoding, to: UTF32.self,
stoppingOnError: true,
into: sink)
_sanityCheck(!hadError,
"string cannot be ASCII if there were decoding errors")
return String(_largeStorage: storage)
} else {
// TODO(SSO): Small transcoded string
let storage = _SwiftStringStorage<UTF16.CodeUnit>.create(
capacity: capacity,
count: utf16Count)
var p = storage.start
let sink: (UTF16.CodeUnit) -> Void = {
p.pointee = $0
p += 1
}
_ = transcode(
input.makeIterator(),
from: encoding, to: UTF16.self,
stoppingOnError: !repairIllFormedSequences,
into: sink)
return String(_largeStorage: storage)
}
}
internal static func _fromNonASCIIUTF8(
_ input: UnsafeBufferPointer<UInt8>, repair: Bool
) -> String? {
if let smol = _SmallUTF8String(input) {
return String(_StringGuts(smol))
}
// Determine how many UTF-16 code units we'll need
let inputStream = input.makeIterator()
// TODO: Replace with much, much faster length check
guard let (utf16Count, isASCII) = UTF16.transcodedLength(
of: inputStream,
decodedAs: UTF8.self,
repairingIllFormedSequences: repair) else {
return nil
}
let capacity = utf16Count
_sanityCheck(!isASCII, "was given ASCII UTF-8")
let storage = _SwiftStringStorage<UTF16.CodeUnit>.create(
capacity: capacity,
count: utf16Count)
var p = storage.start
let sink: (UTF16.CodeUnit) -> Void = {
p.pointee = $0
p += 1
}
// TODO: Replace with much, much faster transcoding
_ = transcode(
input.makeIterator(),
from: UTF8.self, to: UTF16.self,
stoppingOnError: !repair,
into: sink)
return String(_largeStorage: storage)
}
// For testing purposes only, allow ourselves to have invalid contents
@usableFromInline // @testable
static internal
func _fromInvalidUTF16(_ cus: UnsafeBufferPointer<UInt16>) -> String {
let storage = _SwiftStringStorage<UTF16.CodeUnit>.create(
capacity: cus.count, count: cus.count)
_ = storage._initialize(fromCodeUnits: cus, encoding: UTF16.self)
return String(_StringGuts(_large: storage))
}
}