Avoid StringUTF16View dispatch overhead for some bridged String methods (#83529)

This removes a bunch of overhead on the UTF16 paths in String, as well
as consolidating the complicated bits of the logic in one file.
This commit is contained in:
David Smith
2025-09-22 17:03:24 -07:00
committed by GitHub
parent b250ef7be3
commit 7b78a1d4b4
7 changed files with 104 additions and 49 deletions

View File

@@ -43,25 +43,17 @@ extension String {
// ObjC interfaces.
extension _AbstractStringStorage {
@inline(__always)
@_effects(releasenone)
internal func _getCharacters(
_ buffer: UnsafeMutablePointer<UInt16>, _ aRange: _SwiftNSRange
) {
_precondition(aRange.location >= 0 && aRange.length >= 0,
"Range out of bounds")
// Note: `count` is counting UTF-8 code units, while `aRange` is measured in
// UTF-16 offsets. This precondition is a necessary, but not sufficient test
// for validity. (More precise checks are done in UTF16View._nativeCopy.)
_precondition(aRange.location + aRange.length <= Int(count),
"Range out of bounds")
let range = unsafe Range(
_uncheckedBounds: (aRange.location, aRange.location+aRange.length))
let str = asString
unsafe str._copyUTF16CodeUnits(
unsafe utf16._nativeCopy(
into: UnsafeMutableBufferPointer(start: buffer, count: range.count),
range: range)
offsetRange: range)
}
@inline(__always)
@@ -116,6 +108,26 @@ extension _AbstractStringStorage {
return _cocoaLengthOfBytesInEncodingTrampoline(self, encoding)
}
}
// The caller info isn't useful here anyway because it's never client code,
// so this makes sure that _character(at:) doesn't have inlined assertion bits
@inline(never)
internal func _characterAtIndexOutOfBounds() -> Never {
_preconditionFailure("String index is out of bounds")
}
@inline(__always)
@_effects(readonly)
internal func _character(at offset: Int) -> UInt16 {
if _fastPath(isASCII) {
if (_fastPath(offset < count && offset >= 0)) {
return unsafe UInt16((start + offset).pointee)
}
_characterAtIndexOutOfBounds()
} else {
return utf16[nativeNonASCIIOffset: offset]
}
}
@_effects(readonly)
internal func _nativeIsEqual<T:_AbstractStringStorage>(
@@ -176,7 +188,7 @@ extension _AbstractStringStorage {
start: utf16Ptr,
count: otherUTF16Length
)
return unsafe asString.utf16.elementsEqual(utf16Buffer) ? 1 : 0
return unsafe utf16.elementsEqual(utf16Buffer) ? 1 : 0
}
/*
@@ -197,7 +209,7 @@ extension __StringStorage {
if isASCII {
return count
}
return asString.utf16.count
return utf16.count
}
}
@@ -214,8 +226,7 @@ extension __StringStorage {
@objc(characterAtIndex:)
@_effects(readonly)
final internal func character(at offset: Int) -> UInt16 {
let str = asString
return str.utf16[str._toUTF16Index(offset)]
_character(at: offset)
}
@objc(getCharacters:range:)
@@ -313,7 +324,7 @@ extension __SharedStringStorage {
if isASCII {
return count
}
return asString.utf16.count
return utf16.count
}
}
@@ -330,8 +341,7 @@ extension __SharedStringStorage {
@objc(characterAtIndex:)
@_effects(readonly)
final internal func character(at offset: Int) -> UInt16 {
let str = asString
return str.utf16[str._toUTF16Index(offset)]
_character(at: offset)
}
@objc(getCharacters:range:)