mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
809 lines
24 KiB
Swift
809 lines
24 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftShims
|
|
|
|
/// Effectively an untyped NSString that doesn't require foundation.
|
|
@usableFromInline
|
|
internal typealias _CocoaString = AnyObject
|
|
|
|
#if _runtime(_ObjC)
|
|
|
|
// Swift's String bridges NSString via this protocol and these
|
|
// variables, allowing the core stdlib to remain decoupled from
|
|
// Foundation.
|
|
|
|
@objc private protocol _StringSelectorHolder : _NSCopying {
|
|
|
|
@objc var length: Int { get }
|
|
|
|
@objc var hash: UInt { get }
|
|
|
|
@objc(characterAtIndex:)
|
|
func character(at offset: Int) -> UInt16
|
|
|
|
@objc(getCharacters:range:)
|
|
func getCharacters(
|
|
_ buffer: UnsafeMutablePointer<UInt16>, range aRange: _SwiftNSRange
|
|
)
|
|
|
|
@objc(_fastCStringContents:)
|
|
func _fastCStringContents(
|
|
_ requiresNulTermination: Int8
|
|
) -> UnsafePointer<CChar>?
|
|
|
|
@objc(_fastCharacterContents)
|
|
func _fastCharacterContents() -> UnsafePointer<UInt16>?
|
|
|
|
@objc(getBytes:maxLength:usedLength:encoding:options:range:remainingRange:)
|
|
func getBytes(_ buffer: UnsafeMutableRawPointer?,
|
|
maxLength maxBufferCount: Int,
|
|
usedLength usedBufferCount: UnsafeMutablePointer<Int>?,
|
|
encoding: UInt,
|
|
options: UInt,
|
|
range: _SwiftNSRange,
|
|
remaining leftover: UnsafeMutablePointer<_SwiftNSRange>?) -> Int8
|
|
|
|
@objc(compare:options:range:locale:)
|
|
func compare(_ string: _CocoaString,
|
|
options: UInt,
|
|
range: _SwiftNSRange,
|
|
locale: AnyObject?) -> Int
|
|
|
|
@objc(newTaggedNSStringWithASCIIBytes_:length_:)
|
|
func createTaggedString(bytes: UnsafePointer<UInt8>,
|
|
count: Int) -> AnyObject?
|
|
|
|
@objc(isNSString__)
|
|
func getIsNSString() -> Int8
|
|
}
|
|
|
|
/*
|
|
Passing a _CocoaString through _objc() lets you call ObjC methods that the
|
|
compiler doesn't know about, via the protocol above. In order to get good
|
|
performance, you need a double indirection like this:
|
|
|
|
func a -> _objc -> func a'
|
|
|
|
because any refcounting @_effects on 'a' will be lost when _objc breaks ARC's
|
|
knowledge that the _CocoaString and _StringSelectorHolder are the same object.
|
|
*/
|
|
@inline(__always)
|
|
private func _objc(_ str: _CocoaString) -> _StringSelectorHolder {
|
|
return unsafe unsafeBitCast(str, to: _StringSelectorHolder.self)
|
|
}
|
|
|
|
@_effects(releasenone)
|
|
private func _copyNSString(_ str: _StringSelectorHolder) -> _CocoaString {
|
|
return unsafe str.copy(with: nil)
|
|
}
|
|
|
|
@usableFromInline // @testable
|
|
@_effects(releasenone)
|
|
internal func _stdlib_binary_CFStringCreateCopy(
|
|
_ source: _CocoaString
|
|
) -> _CocoaString {
|
|
return _copyNSString(_objc(source))
|
|
}
|
|
|
|
@_effects(readonly)
|
|
private func _NSStringLen(_ str: _StringSelectorHolder) -> Int {
|
|
return str.length
|
|
}
|
|
|
|
@usableFromInline // @testable
|
|
@_effects(readonly)
|
|
internal func _stdlib_binary_CFStringGetLength(
|
|
_ source: _CocoaString
|
|
) -> Int {
|
|
return _NSStringLen(_objc(source))
|
|
}
|
|
|
|
@_effects(readonly) private func isNSStringImpl(
|
|
_ str: _StringSelectorHolder
|
|
) -> Bool {
|
|
return str.getIsNSString() != 0
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _isNSString(_ str:AnyObject) -> Bool {
|
|
return isNSStringImpl(_objc(str))
|
|
}
|
|
|
|
@_effects(readonly)
|
|
private func _NSStringCharactersPtr(_ str: _StringSelectorHolder) -> UnsafeMutablePointer<UTF16.CodeUnit>? {
|
|
return unsafe UnsafeMutablePointer(mutating: str._fastCharacterContents())
|
|
}
|
|
|
|
private func _stdlib_binary_createIndirectTaggedPointerNSString(
|
|
ptr: UnsafePointer<UInt8>,
|
|
count: Int
|
|
) -> UnsafeRawPointer? {
|
|
return unsafe _swift_stdlib_CreateIndirectTaggedPointerString(ptr, count);
|
|
}
|
|
|
|
@usableFromInline // @testable
|
|
@_effects(readonly)
|
|
internal func _stdlib_binary_CFStringGetCharactersPtr(
|
|
_ source: _CocoaString
|
|
) -> UnsafeMutablePointer<UTF16.CodeUnit>? {
|
|
return unsafe _NSStringCharactersPtr(_objc(source))
|
|
}
|
|
|
|
@_effects(releasenone)
|
|
private func _NSStringGetCharacters(
|
|
from source: _StringSelectorHolder,
|
|
range: Range<Int>,
|
|
into destination: UnsafeMutablePointer<UTF16.CodeUnit>
|
|
) {
|
|
unsafe source.getCharacters(destination, range: _SwiftNSRange(
|
|
location: range.startIndex,
|
|
length: range.count)
|
|
)
|
|
}
|
|
|
|
/// Copies a slice of a _CocoaString into contiguous storage of sufficient
|
|
/// capacity.
|
|
@_effects(releasenone)
|
|
internal func _cocoaStringCopyCharacters(
|
|
from source: _CocoaString,
|
|
range: Range<Int>,
|
|
into destination: UnsafeMutablePointer<UTF16.CodeUnit>
|
|
) {
|
|
unsafe _NSStringGetCharacters(from: _objc(source), range: range, into: destination)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
private func _NSStringGetCharacter(
|
|
_ target: _StringSelectorHolder, _ position: Int
|
|
) -> UTF16.CodeUnit {
|
|
return target.character(at: position)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _cocoaStringSubscript(
|
|
_ target: _CocoaString, _ position: Int
|
|
) -> UTF16.CodeUnit {
|
|
return _NSStringGetCharacter(_objc(target), position)
|
|
}
|
|
|
|
@_effects(releasenone)
|
|
private func _NSStringCopyBytes(
|
|
_ o: _StringSelectorHolder,
|
|
encoding: UInt,
|
|
into bufPtr: UnsafeMutableRawBufferPointer
|
|
) -> Int? {
|
|
let ptr = unsafe bufPtr.baseAddress._unsafelyUnwrappedUnchecked
|
|
let len = o.length
|
|
var remainingRange = _SwiftNSRange(location: 0, length: 0)
|
|
var usedLen = 0
|
|
let success = unsafe 0 != o.getBytes(
|
|
ptr,
|
|
maxLength: bufPtr.count,
|
|
usedLength: &usedLen,
|
|
encoding: encoding,
|
|
options: 0,
|
|
range: _SwiftNSRange(location: 0, length: len),
|
|
remaining: &remainingRange
|
|
)
|
|
if success && remainingRange.length == 0 {
|
|
return usedLen
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@_effects(releasenone)
|
|
internal func _cocoaStringCopyUTF8(
|
|
_ target: _CocoaString,
|
|
into bufPtr: UnsafeMutableRawBufferPointer
|
|
) -> Int? {
|
|
return unsafe _NSStringCopyBytes(
|
|
_objc(target),
|
|
encoding: _cocoaUTF8Encoding,
|
|
into: bufPtr
|
|
)
|
|
}
|
|
|
|
@_effects(releasenone)
|
|
internal func _cocoaStringCopyASCII(
|
|
_ target: _CocoaString,
|
|
into bufPtr: UnsafeMutableRawBufferPointer
|
|
) -> Int? {
|
|
return unsafe _NSStringCopyBytes(
|
|
_objc(target),
|
|
encoding: _cocoaASCIIEncoding,
|
|
into: bufPtr
|
|
)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
private func _NSStringUTF8Count(
|
|
_ o: _StringSelectorHolder,
|
|
range: Range<Int>
|
|
) -> Int? {
|
|
var remainingRange = _SwiftNSRange(location: 0, length: 0)
|
|
var usedLen = 0
|
|
let success = unsafe 0 != o.getBytes(
|
|
UnsafeMutableRawPointer(Builtin.inttoptr_Word(0._builtinWordValue)),
|
|
maxLength: 0,
|
|
usedLength: &usedLen,
|
|
encoding: _cocoaUTF8Encoding,
|
|
options: 0,
|
|
range: _SwiftNSRange(location: range.startIndex, length: range.count),
|
|
remaining: &remainingRange
|
|
)
|
|
if success && remainingRange.length == 0 {
|
|
return usedLen
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _cocoaStringUTF8Count(
|
|
_ target: _CocoaString,
|
|
range: Range<Int>
|
|
) -> Int? {
|
|
if range.isEmpty { return 0 }
|
|
return _NSStringUTF8Count(_objc(target), range: range)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
private func _NSStringCompare(
|
|
_ o: _StringSelectorHolder, _ other: _CocoaString
|
|
) -> Int {
|
|
let range = _SwiftNSRange(location: 0, length: o.length)
|
|
let options = UInt(2) /* NSLiteralSearch*/
|
|
return o.compare(other, options: options, range: range, locale: nil)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _cocoaStringCompare(
|
|
_ string: _CocoaString, _ other: _CocoaString
|
|
) -> Int {
|
|
return _NSStringCompare(_objc(string), other)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _cocoaHashString(
|
|
_ string: _CocoaString
|
|
) -> UInt {
|
|
return _swift_stdlib_CFStringHashNSString(string)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _cocoaHashASCIIBytes(
|
|
_ bytes: UnsafePointer<UInt8>, length: Int
|
|
) -> UInt {
|
|
return unsafe _swift_stdlib_CFStringHashCString(bytes, length)
|
|
}
|
|
|
|
// These "trampolines" are effectively objc_msgSend_super.
|
|
// They bypass our implementations to use NSString's.
|
|
|
|
@_effects(readonly)
|
|
internal func _cocoaCStringUsingEncodingTrampoline(
|
|
_ string: _CocoaString, _ encoding: UInt
|
|
) -> UnsafePointer<UInt8>? {
|
|
return unsafe _swift_stdlib_NSStringCStringUsingEncodingTrampoline(string, encoding)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _cocoaLengthOfBytesInEncodingTrampoline(
|
|
_ string: _CocoaString, _ encoding: UInt
|
|
) -> UInt {
|
|
return _swift_stdlib_NSStringLengthOfBytesInEncodingTrampoline(string, encoding)
|
|
}
|
|
|
|
@_effects(releasenone)
|
|
internal func _cocoaGetCStringTrampoline(
|
|
_ string: _CocoaString,
|
|
_ buffer: UnsafeMutablePointer<UInt8>,
|
|
_ maxLength: Int,
|
|
_ encoding: UInt
|
|
) -> Int8 {
|
|
return unsafe Int8(_swift_stdlib_NSStringGetCStringTrampoline(
|
|
string, buffer, maxLength, encoding))
|
|
}
|
|
|
|
//
|
|
// Conversion from NSString to Swift's native representation.
|
|
//
|
|
|
|
private var kCFStringEncodingASCII: _swift_shims_CFStringEncoding {
|
|
@inline(__always) get { return 0x0600 }
|
|
}
|
|
|
|
private var kCFStringEncodingUTF8: _swift_shims_CFStringEncoding {
|
|
@inline(__always) get { return 0x8000100 }
|
|
}
|
|
|
|
internal enum _KnownCocoaString {
|
|
case storage
|
|
case shared
|
|
case cocoa
|
|
#if _pointerBitWidth(_64)
|
|
case tagged
|
|
#endif
|
|
|
|
@inline(__always)
|
|
init(_ str: _CocoaString) {
|
|
|
|
#if _pointerBitWidth(_64)
|
|
if _isObjCTaggedPointer(str) {
|
|
self = .tagged
|
|
return
|
|
}
|
|
#endif
|
|
|
|
switch unsafe unsafeBitCast(_swift_classOfObjCHeapObject(str), to: UInt.self) {
|
|
case unsafe unsafeBitCast(__StringStorage.self, to: UInt.self):
|
|
self = .storage
|
|
case unsafe unsafeBitCast(__SharedStringStorage.self, to: UInt.self):
|
|
self = .shared
|
|
default:
|
|
self = .cocoa
|
|
}
|
|
}
|
|
}
|
|
|
|
#if _pointerBitWidth(_64)
|
|
// Resiliently write a tagged _CocoaString's contents into a buffer.
|
|
// The Foundation overlay takes care of bridging tagged pointer strings before
|
|
// they reach us, but this may still be called by older code, or by strings
|
|
// entering our domain via the arguments to -isEqual:, etc...
|
|
@_effects(releasenone) // @opaque
|
|
internal func _bridgeTagged(
|
|
_ cocoa: _CocoaString,
|
|
intoUTF8 bufPtr: UnsafeMutableRawBufferPointer
|
|
) -> Int? {
|
|
_internalInvariant(_isObjCTaggedPointer(cocoa))
|
|
return unsafe _cocoaStringCopyUTF8(cocoa, into: bufPtr)
|
|
}
|
|
|
|
@_effects(releasenone) // @opaque
|
|
internal func _bridgeTaggedASCII(
|
|
_ cocoa: _CocoaString,
|
|
intoUTF8 bufPtr: UnsafeMutableRawBufferPointer
|
|
) -> Int? {
|
|
_internalInvariant(_isObjCTaggedPointer(cocoa))
|
|
return unsafe _cocoaStringCopyASCII(cocoa, into: bufPtr)
|
|
}
|
|
#endif
|
|
|
|
@_effects(readonly)
|
|
private func _NSStringASCIIPointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
|
|
//TODO(String bridging): Unconditionally asking for nul-terminated contents is
|
|
// overly conservative and hurts perf with some NSStrings
|
|
return unsafe str._fastCStringContents(1)?._asUInt8
|
|
}
|
|
|
|
@_effects(readonly)
|
|
private func _NSStringUTF8Pointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
|
|
//We don't have a way to ask for UTF8 here currently
|
|
return unsafe _NSStringASCIIPointer(str)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func _getNSCFConstantStringContentsPointer(
|
|
_ cocoa: AnyObject
|
|
) -> UnsafePointer<UInt8> {
|
|
return unsafe unsafeBitCast(
|
|
cocoa,
|
|
to: UnsafePointer<_swift_shims_builtin_CFString>.self
|
|
).pointee.str
|
|
}
|
|
|
|
@_effects(readonly) // @opaque
|
|
private func _withCocoaASCIIPointer<R>(
|
|
_ str: _CocoaString,
|
|
requireStableAddress: Bool,
|
|
work: (UnsafePointer<UInt8>) -> R?
|
|
) -> R? {
|
|
#if _pointerBitWidth(_64)
|
|
if _isObjCTaggedPointer(str) {
|
|
if requireStableAddress {
|
|
return nil // tagged pointer strings don't support _fastCStringContents
|
|
}
|
|
if let smol = _SmallString(taggedASCIICocoa: str) {
|
|
return unsafe _StringGuts(smol).withFastUTF8 {
|
|
unsafe work($0.baseAddress._unsafelyUnwrappedUnchecked)
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
defer { _fixLifetime(str) }
|
|
if let ptr = unsafe _NSStringASCIIPointer(_objc(str)) {
|
|
return unsafe work(ptr)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@_effects(readonly) // @opaque
|
|
private func _withCocoaUTF8Pointer<R>(
|
|
_ str: _CocoaString,
|
|
requireStableAddress: Bool,
|
|
work: (UnsafePointer<UInt8>) -> R?
|
|
) -> R? {
|
|
#if _pointerBitWidth(_64)
|
|
if _isObjCTaggedPointer(str) {
|
|
if requireStableAddress {
|
|
return nil // tagged pointer strings don't support _fastCStringContents
|
|
}
|
|
if let smol = _SmallString(taggedCocoa: str) {
|
|
return unsafe _StringGuts(smol).withFastUTF8 {
|
|
unsafe work($0.baseAddress._unsafelyUnwrappedUnchecked)
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
defer { _fixLifetime(str) }
|
|
if let ptr = unsafe _NSStringUTF8Pointer(_objc(str)) {
|
|
return unsafe work(ptr)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@_effects(readonly) // @opaque
|
|
internal func withCocoaASCIIPointer<R>(
|
|
_ str: _CocoaString,
|
|
work: (UnsafePointer<UInt8>) -> R?
|
|
) -> R? {
|
|
return unsafe _withCocoaASCIIPointer(str, requireStableAddress: false, work: work)
|
|
}
|
|
|
|
@_effects(readonly) // @opaque
|
|
internal func withCocoaUTF8Pointer<R>(
|
|
_ str: _CocoaString,
|
|
work: (UnsafePointer<UInt8>) -> R?
|
|
) -> R? {
|
|
return unsafe _withCocoaUTF8Pointer(str, requireStableAddress: false, work: work)
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func stableCocoaASCIIPointer(_ str: _CocoaString)
|
|
-> UnsafePointer<UInt8>? {
|
|
return unsafe _withCocoaASCIIPointer(str, requireStableAddress: true, work: { unsafe $0 })
|
|
}
|
|
|
|
@_effects(readonly)
|
|
internal func stableCocoaUTF8Pointer(_ str: _CocoaString)
|
|
-> UnsafePointer<UInt8>? {
|
|
return unsafe _withCocoaUTF8Pointer(str, requireStableAddress: true, work: { unsafe $0 })
|
|
}
|
|
|
|
@unsafe
|
|
private enum CocoaStringPointer {
|
|
case ascii(UnsafePointer<UInt8>)
|
|
case utf8(UnsafePointer<UInt8>)
|
|
case utf16(UnsafePointer<UInt16>)
|
|
case none
|
|
}
|
|
|
|
@_effects(readonly)
|
|
private func _getCocoaStringPointer(
|
|
_ cfImmutableValue: _CocoaString
|
|
) -> CocoaStringPointer {
|
|
if let ascii = unsafe stableCocoaASCIIPointer(cfImmutableValue) {
|
|
return unsafe .ascii(ascii)
|
|
}
|
|
// We could ask for UTF16 here via _stdlib_binary_CFStringGetCharactersPtr,
|
|
// but we currently have no use for it
|
|
return unsafe .none
|
|
}
|
|
|
|
#if !$Embedded
|
|
@usableFromInline
|
|
@_effects(releasenone) // @opaque
|
|
internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts {
|
|
switch _KnownCocoaString(cocoaString) {
|
|
case .storage:
|
|
return unsafe _unsafeUncheckedDowncast(
|
|
cocoaString, to: __StringStorage.self).asString._guts
|
|
case .shared:
|
|
return unsafe _unsafeUncheckedDowncast(
|
|
cocoaString, to: __SharedStringStorage.self).asString._guts
|
|
#if _pointerBitWidth(_64)
|
|
case .tagged:
|
|
// Foundation should be taking care of tagged pointer strings before they
|
|
// reach here, so the only ones reaching this point should be back deployed,
|
|
// which will never have tagged pointer strings that aren't small, hence
|
|
// the force unwrap here.
|
|
return _StringGuts(_SmallString(taggedCocoa: cocoaString)!)
|
|
#endif
|
|
case .cocoa:
|
|
// "Copy" it into a value to be sure nobody will modify behind
|
|
// our backs. In practice, when value is already immutable, this
|
|
// just does a retain.
|
|
//
|
|
// TODO: Only in certain circumstances should we emit this call:
|
|
// 1) If it's immutable, just retain it.
|
|
// 2) If it's mutable with no associated information, then a copy must
|
|
// happen; might as well eagerly bridge it in.
|
|
// 3) If it's mutable with associated information, must make the call
|
|
let immutableCopy
|
|
= _stdlib_binary_CFStringCreateCopy(cocoaString)
|
|
|
|
#if _pointerBitWidth(_64)
|
|
if _isObjCTaggedPointer(immutableCopy) {
|
|
// Copying a tagged pointer can produce a tagged pointer, but only if it's
|
|
// small enough to definitely fit in a _SmallString
|
|
return unsafe _StringGuts(
|
|
_SmallString(taggedCocoa: immutableCopy).unsafelyUnwrapped
|
|
)
|
|
}
|
|
#endif
|
|
|
|
let (fastUTF8, isASCII): (Bool, Bool)
|
|
switch unsafe _getCocoaStringPointer(immutableCopy) {
|
|
case .ascii(_): (fastUTF8, isASCII) = (true, true)
|
|
case .utf8(_): (fastUTF8, isASCII) = (true, false)
|
|
default: (fastUTF8, isASCII) = (false, false)
|
|
}
|
|
let length = _stdlib_binary_CFStringGetLength(immutableCopy)
|
|
|
|
return _StringGuts(
|
|
cocoa: immutableCopy,
|
|
providesFastUTF8: fastUTF8,
|
|
isASCII: isASCII,
|
|
length: length)
|
|
}
|
|
}
|
|
|
|
extension String {
|
|
@available(SwiftStdlib 6.1, *)
|
|
@_spi(Foundation)
|
|
public init<Encoding: Unicode.Encoding>(
|
|
_immortalCocoaString: AnyObject,
|
|
count: Int,
|
|
encoding: Encoding.Type
|
|
) {
|
|
if encoding == Unicode.ASCII.self || encoding == Unicode.UTF8.self {
|
|
self._guts = _StringGuts(
|
|
constantCocoa: _immortalCocoaString,
|
|
providesFastUTF8: true,
|
|
isASCII: encoding == Unicode.ASCII.self,
|
|
length: count)
|
|
} else {
|
|
_precondition(encoding == Unicode.UTF16.self)
|
|
// Only need the very last bit of _bridgeCocoaString here,
|
|
// since we know the fast paths don't apply
|
|
self._guts = _StringGuts(
|
|
cocoa: _immortalCocoaString,
|
|
providesFastUTF8: false,
|
|
isASCII: false,
|
|
length: count)
|
|
}
|
|
}
|
|
|
|
@_spi(Foundation)
|
|
public init(_cocoaString: AnyObject) {
|
|
self._guts = _bridgeCocoaString(_cocoaString)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
@_effects(releasenone)
|
|
private func _createNSString(
|
|
_ receiver: _StringSelectorHolder,
|
|
_ ptr: UnsafePointer<UInt8>,
|
|
_ count: Int,
|
|
_ encoding: UInt32
|
|
) -> AnyObject? {
|
|
return unsafe receiver.createTaggedString(bytes: ptr, count: count)
|
|
}
|
|
|
|
@_effects(releasenone)
|
|
private func _createCFString(
|
|
_ ptr: UnsafePointer<UInt8>,
|
|
_ count: Int,
|
|
_ encoding: UInt32
|
|
) -> AnyObject? {
|
|
return unsafe _createNSString(
|
|
unsafeBitCast(__StringStorage.self as AnyClass, to: _StringSelectorHolder.self),
|
|
ptr,
|
|
count,
|
|
encoding
|
|
)
|
|
}
|
|
|
|
#if !$Embedded
|
|
extension String {
|
|
@_effects(releasenone)
|
|
public // SPI(Foundation)
|
|
func _bridgeToObjectiveCImpl() -> AnyObject {
|
|
|
|
_connectOrphanedFoundationSubclassesIfNeeded()
|
|
|
|
if _guts.isSmall {
|
|
return _guts.asSmall.withUTF8 { bufPtr in
|
|
// Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
|
|
if _guts.isSmallASCII, let result = unsafe _createCFString(
|
|
bufPtr.baseAddress._unsafelyUnwrappedUnchecked,
|
|
bufPtr.count,
|
|
kCFStringEncodingUTF8
|
|
) {
|
|
return result
|
|
}
|
|
// We can't form a tagged pointer String, so make a non-small String,
|
|
// and bridge that instead. Also avoids CF deleting any BOM that may be
|
|
// present
|
|
return unsafe __StringStorage.create(
|
|
initializingFrom: bufPtr,
|
|
codeUnitCapacity: bufPtr.count,
|
|
isASCII: _guts.isSmallASCII)
|
|
}
|
|
}
|
|
if _guts._object.isImmortal && !_guts._object.largeFastIsConstantCocoa {
|
|
if _guts.isASCII && _guts._object.isFastZeroTerminated {
|
|
let ptr = unsafe _guts._object.fastUTF8.baseAddress._unsafelyUnwrappedUnchecked
|
|
let count = _guts.count
|
|
if let indirect = unsafe _stdlib_binary_createIndirectTaggedPointerNSString(
|
|
ptr: ptr, count: count
|
|
) {
|
|
return unsafe unsafeBitCast(indirect, to: AnyObject.self)
|
|
}
|
|
}
|
|
let gutsCountAndFlags = _guts._object._countAndFlags
|
|
return unsafe __SharedStringStorage(
|
|
immortal: _guts._object.fastUTF8.baseAddress!,
|
|
countAndFlags: _StringObject.CountAndFlags(
|
|
sharedCount: _guts.count, isASCII: gutsCountAndFlags.isASCII))
|
|
}
|
|
|
|
_internalInvariant(_guts._object.hasObjCBridgeableObject,
|
|
"Unknown non-bridgeable object case")
|
|
return _guts._object.objCBridgeableObject
|
|
}
|
|
}
|
|
|
|
// Note: This function is not intended to be called from Swift. The
|
|
// availability information here is perfunctory; this function isn't considered
|
|
// part of the Stdlib's Swift ABI.
|
|
@available(SwiftStdlib 5.2, *)
|
|
@_cdecl("_SwiftCreateBridgedString")
|
|
@usableFromInline
|
|
internal func _SwiftCreateBridgedString_DoNotCall(
|
|
bytes: UnsafePointer<UInt8>,
|
|
length: Int,
|
|
encoding: _swift_shims_CFStringEncoding
|
|
) -> Unmanaged<AnyObject> {
|
|
let bufPtr = unsafe UnsafeBufferPointer(start: bytes, count: length)
|
|
let str:String
|
|
switch encoding {
|
|
case kCFStringEncodingUTF8:
|
|
str = unsafe String(decoding: bufPtr, as: Unicode.UTF8.self)
|
|
case kCFStringEncodingASCII:
|
|
str = unsafe String(decoding: bufPtr, as: Unicode.ASCII.self)
|
|
default:
|
|
fatalError("Unsupported encoding in shim")
|
|
}
|
|
return unsafe Unmanaged<AnyObject>.passRetained(str._bridgeToObjectiveCImpl())
|
|
}
|
|
|
|
@available(SwiftStdlib 6.1, *)
|
|
@_spi(Foundation) public func _SwiftCreateImmortalString_ForFoundation(
|
|
buffer: UnsafeBufferPointer<UInt8>,
|
|
isASCII: Bool
|
|
) -> String? {
|
|
switch unsafe validateUTF8(buffer) {
|
|
case .success(let extraInfo):
|
|
return unsafe String(_StringGuts(buffer, isASCII: extraInfo.isASCII))
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// At runtime, this class is derived from `__SwiftNativeNSStringBase`,
|
|
// which is derived from `NSString`.
|
|
//
|
|
// The @_swift_native_objc_runtime_base attribute
|
|
// This allows us to subclass an Objective-C class and use the fast Swift
|
|
// memory allocator.
|
|
@objc @_swift_native_objc_runtime_base(__SwiftNativeNSStringBase)
|
|
@_spi(Foundation) public class __SwiftNativeNSString {
|
|
@objc internal init() {}
|
|
deinit {}
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
extension __SwiftNativeNSString: Sendable {}
|
|
|
|
// Called by the SwiftObject implementation to get the description of a value
|
|
// as an NSString.
|
|
@_silgen_name("swift_stdlib_getDescription")
|
|
public func _getDescription<T>(_ x: T) -> AnyObject {
|
|
return String(reflecting: x)._bridgeToObjectiveCImpl()
|
|
}
|
|
|
|
@_silgen_name("swift_stdlib_NSStringFromUTF8")
|
|
@usableFromInline //this makes the symbol available to the runtime :(
|
|
@available(SwiftStdlib 5.2, *)
|
|
internal func _NSStringFromUTF8(_ s: UnsafePointer<UInt8>, _ len: Int)
|
|
-> AnyObject {
|
|
return unsafe String(
|
|
decoding: UnsafeBufferPointer(start: s, count: len),
|
|
as: UTF8.self
|
|
)._bridgeToObjectiveCImpl()
|
|
}
|
|
#endif
|
|
|
|
#else // !_runtime(_ObjC)
|
|
|
|
internal class __SwiftNativeNSString {
|
|
internal init() {}
|
|
deinit {}
|
|
}
|
|
|
|
#endif
|
|
|
|
// Special-case Index <-> Offset converters for bridging and use in accelerating
|
|
// the UTF16View in general.
|
|
extension StringProtocol {
|
|
@_specialize(where Self == String)
|
|
@_specialize(where Self == Substring)
|
|
public // SPI(Foundation)
|
|
func _toUTF16Offset(_ idx: Index) -> Int {
|
|
return self.utf16.distance(from: self.utf16.startIndex, to: idx)
|
|
}
|
|
|
|
@_specialize(where Self == String)
|
|
@_specialize(where Self == Substring)
|
|
public // SPI(Foundation)
|
|
func _toUTF16Index(_ offset: Int) -> Index {
|
|
return self.utf16.index(self.utf16.startIndex, offsetBy: offset)
|
|
}
|
|
|
|
public // SPI(Foundation)
|
|
func _toUTF16Offsets(_ indices: Range<Index>) -> Range<Int> {
|
|
if Self.self == String.self {
|
|
let s = unsafe unsafeBitCast(self, to: String.self)
|
|
return s.utf16._offsetRange(for: indices, from: s.startIndex)
|
|
}
|
|
if Self.self == Substring.self {
|
|
let s = unsafe unsafeBitCast(self, to: Substring.self)
|
|
return s._slice._base.utf16._offsetRange(for: indices, from: s.startIndex)
|
|
}
|
|
let startOffset = _toUTF16Offset(indices.lowerBound)
|
|
let endOffset = _toUTF16Offset(indices.upperBound)
|
|
return unsafe Range(uncheckedBounds: (lower: startOffset, upper: endOffset))
|
|
}
|
|
|
|
public // SPI(Foundation)
|
|
func _toUTF16Indices(_ range: Range<Int>) -> Range<Index> {
|
|
#if hasFeature(Macros)
|
|
if Self.self == String.self {
|
|
let s = unsafe unsafeBitCast(self, to: String.self)
|
|
return s.utf16._indexRange(for: range, from: s.startIndex)
|
|
}
|
|
if Self.self == Substring.self {
|
|
let s = unsafe unsafeBitCast(self, to: Substring.self)
|
|
return s._slice._base.utf16._indexRange(for: range, from: s.startIndex)
|
|
}
|
|
#endif
|
|
let lowerbound = _toUTF16Index(range.lowerBound)
|
|
let upperbound = _toUTF16Index(range.upperBound)
|
|
return unsafe Range(uncheckedBounds: (lower: lowerbound, upper: upperbound))
|
|
}
|
|
}
|
|
|
|
extension String {
|
|
public // @testable / @benchmarkable
|
|
func _copyUTF16CodeUnits(
|
|
into buffer: UnsafeMutableBufferPointer<UInt16>,
|
|
range: Range<Int>
|
|
) {
|
|
_internalInvariant(buffer.count >= range.count)
|
|
let indexRange = self._toUTF16Indices(range)
|
|
unsafe self.utf16._nativeCopy(into: buffer, alignedRange: indexRange)
|
|
}
|
|
}
|