diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index ea06cc77d28d6..c79334fa5bc42 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -62,6 +62,9 @@ internal typealias _CocoaString = AnyObject @objc(newTaggedNSStringWithASCIIBytes_:length_:) func createTaggedString(bytes: UnsafePointer, count: Int) -> AnyObject? + + @objc(isNSString__) + func getIsNSString() -> Int8 } /* @@ -105,9 +108,15 @@ internal func _stdlib_binary_CFStringGetLength( 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 _swift_stdlib_isNSString(str) != 0 + return isNSStringImpl(_objc(str)) } @_effects(readonly) diff --git a/stdlib/public/core/StringStorageBridge.swift b/stdlib/public/core/StringStorageBridge.swift index 5df97e06317e2..8d9a6903f6738 100644 --- a/stdlib/public/core/StringStorageBridge.swift +++ b/stdlib/public/core/StringStorageBridge.swift @@ -158,21 +158,25 @@ extension _AbstractStringStorage { // At this point we've proven that it is a non-Swift NSString let otherUTF16Length = _stdlib_binary_CFStringGetLength(other) - + + if UTF16Length != otherUTF16Length { + return 0 + } + // CFString will only give us ASCII bytes here, but that's fine. // We already handled non-ASCII UTF8 strings earlier since they're Swift. if let asciiEqual = unsafe withCocoaASCIIPointer(other, work: { (ascii) -> Bool in - // UTF16 length == UTF8 length iff ASCII - if otherUTF16Length == self.count { - return unsafe (start == ascii || (memcmp(start, ascii, self.count) == 0)) - } - return false + return unsafe (start == ascii || (memcmp(start, ascii, self.count) == 0)) }) { return asciiEqual ? 1 : 0 } - - if self.UTF16Length != otherUTF16Length { - return 0 + + if let utf16Ptr = unsafe _stdlib_binary_CFStringGetCharactersPtr(other) { + let utf16Buffer = unsafe UnsafeBufferPointer( + start: utf16Ptr, + count: otherUTF16Length + ) + return unsafe asString.utf16.elementsEqual(utf16Buffer) ? 1 : 0 } /* @@ -189,7 +193,11 @@ extension __StringStorage { @objc(length) final internal var UTF16Length: Int { @_effects(readonly) @inline(__always) get { - return asString.utf16.count // UTF16View special-cases ASCII for us. + // UTF16View does this, but there's still a little overhead + if isASCII { + return count + } + return asString.utf16.count } } @@ -235,7 +243,7 @@ extension __StringStorage { _ requiresNulTermination: Int8, _ outUTF8Length: UnsafeMutablePointer ) -> UnsafePointer? { - outUTF8Length.pointee = UInt(count) + unsafe outUTF8Length.pointee = UInt(count) return unsafe start } @@ -301,7 +309,11 @@ extension __SharedStringStorage { @objc(length) final internal var UTF16Length: Int { @_effects(readonly) get { - return asString.utf16.count // UTF16View special-cases ASCII for us. + // UTF16View does this, but there's still a little overhead + if isASCII { + return count + } + return asString.utf16.count } } @@ -363,7 +375,7 @@ extension __SharedStringStorage { _ requiresNulTermination: Int8, _ outUTF8Length: UnsafeMutablePointer ) -> UnsafePointer? { - outUTF8Length.pointee = UInt(count) + unsafe outUTF8Length.pointee = UInt(count) return unsafe start }