//===----------------------------------------------------------------------===// // // 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, start: UnsafeMutablePointer, utf8CodeUnitCount: Int ) { resultStorage.initialize(to: String._fromWellFormedUTF8( UnsafeBufferPointer(start: start, count: utf8CodeUnitCount))) } @usableFromInline static func _fromUTF8( _ input: UnsafeBufferPointer, repair: Bool ) -> String? { if _isAllASCII(input) { return _fromASCII(input) } return _fromNonASCIIUTF8(input, repair: repair) } @usableFromInline static func _fromASCII(_ input: UnsafeBufferPointer) -> String { if let smol = _SmallUTF8String(input) { return String(_StringGuts(smol)) } let storage = _SwiftStringStorage.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, repair: Bool = false ) -> String { return String._fromUTF8(input, repair: repair)! } @inlinable static func _fromWellFormedUTF16CodeUnits( _ 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.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.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, 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.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) -> String { let storage = _SwiftStringStorage.create( capacity: cus.count, count: cus.count) _ = storage._initialize(fromCodeUnits: cus, encoding: UTF16.self) return String(_StringGuts(_large: storage)) } }