//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2019 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 #if _runtime(_ObjC) internal let _cocoaASCIIEncoding:UInt = 1 /* NSASCIIStringEncoding */ internal let _cocoaUTF8Encoding:UInt = 4 /* NSUTF8StringEncoding */ extension String { @available(SwiftStdlib 5.6, *) @_spi(Foundation) public init?(_nativeStorage: AnyObject) { let knownOther = _KnownCocoaString(_nativeStorage) switch knownOther { case .storage: self = _unsafeUncheckedDowncast( _nativeStorage, to: __StringStorage.self ).asString case .shared: self = _unsafeUncheckedDowncast( _nativeStorage, to: __SharedStringStorage.self ).asString default: return nil } } } // ObjC interfaces. extension _AbstractStringStorage { @inline(__always) @_effects(releasenone) internal func _getCharacters( _ buffer: UnsafeMutablePointer, _ aRange: _SwiftNSRange ) { _precondition(aRange.location >= 0 && aRange.length >= 0, "Range out of bounds") _precondition(aRange.location + aRange.length <= Int(count), "Range out of bounds") let range = Range( _uncheckedBounds: (aRange.location, aRange.location+aRange.length)) let str = asString str._copyUTF16CodeUnits( into: UnsafeMutableBufferPointer(start: buffer, count: range.count), range: range) } @inline(__always) @_effects(releasenone) internal func _getCString( _ outputPtr: UnsafeMutablePointer, _ maxLength: Int, _ encoding: UInt ) -> Int8 { switch (encoding, isASCII) { case (_cocoaASCIIEncoding, true), (_cocoaUTF8Encoding, _): guard maxLength >= count + 1 else { return 0 } outputPtr.initialize(from: start, count: count) outputPtr[count] = 0 return 1 default: return _cocoaGetCStringTrampoline(self, outputPtr, maxLength, encoding) } } @inline(__always) @_effects(readonly) internal func _cString(encoding: UInt) -> UnsafePointer? { switch (encoding, isASCII) { case (_cocoaASCIIEncoding, true), (_cocoaUTF8Encoding, _): return start default: return _cocoaCStringUsingEncodingTrampoline(self, encoding) } } @_effects(readonly) internal func _nativeIsEqual( _ nativeOther: T ) -> Int8 { if count != nativeOther.count { return 0 } return (start == nativeOther.start || (memcmp(start, nativeOther.start, count) == 0)) ? 1 : 0 } @inline(__always) @_effects(readonly) internal func _isEqual(_ other: AnyObject?) -> Int8 { guard let other = other else { return 0 } if self === other { return 1 } // Handle the case where both strings were bridged from Swift. // We can't use String.== because it doesn't match NSString semantics. let knownOther = _KnownCocoaString(other) switch knownOther { case .storage: return _nativeIsEqual( _unsafeUncheckedDowncast(other, to: __StringStorage.self)) case .shared: return _nativeIsEqual( _unsafeUncheckedDowncast(other, to: __SharedStringStorage.self)) default: // We're allowed to crash, but for compatibility reasons NSCFString allows // non-strings here. if !_isNSString(other) { return 0 } // At this point we've proven that it is a non-Swift NSString let otherUTF16Length = _stdlib_binary_CFStringGetLength(other) // 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 = withCocoaASCIIPointer(other, work: { (ascii) -> Bool in // UTF16 length == UTF8 length iff ASCII if otherUTF16Length == self.count { return (start == ascii || (memcmp(start, ascii, self.count) == 0)) } return false }) { return asciiEqual ? 1 : 0 } if self.UTF16Length != otherUTF16Length { return 0 } /* The abstract implementation of -isEqualToString: falls back to -compare: immediately, so when we run out of fast options to try, do the same. We can likely be more clever here if need be */ return _cocoaStringCompare(self, other) == 0 ? 1 : 0 } } } extension __StringStorage { @objc(length) final internal var UTF16Length: Int { @_effects(readonly) @inline(__always) get { return asString.utf16.count // UTF16View special-cases ASCII for us. } } @objc final internal var hash: UInt { @_effects(readonly) get { if isASCII { return _cocoaHashASCIIBytes(start, length: count) } return _cocoaHashString(self) } } @objc(characterAtIndex:) @_effects(readonly) final internal func character(at offset: Int) -> UInt16 { let str = asString return str.utf16[str._toUTF16Index(offset)] } @objc(getCharacters:range:) @_effects(releasenone) final internal func getCharacters( _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange ) { _getCharacters(buffer, aRange) } @objc(_fastCStringContents:) @_effects(readonly) final internal func _fastCStringContents( _ requiresNulTermination: Int8 ) -> UnsafePointer? { if isASCII { return start._asCChar } return nil } @objc(UTF8String) @_effects(readonly) final internal func _utf8String() -> UnsafePointer? { return start } @objc(cStringUsingEncoding:) @_effects(readonly) final internal func cString(encoding: UInt) -> UnsafePointer? { return _cString(encoding: encoding) } @objc(getCString:maxLength:encoding:) @_effects(releasenone) final internal func getCString( _ outputPtr: UnsafeMutablePointer, maxLength: Int, encoding: UInt ) -> Int8 { return _getCString(outputPtr, maxLength, encoding) } @objc final internal var fastestEncoding: UInt { @_effects(readonly) get { if isASCII { return _cocoaASCIIEncoding } return _cocoaUTF8Encoding } } @objc(isEqualToString:) @_effects(readonly) final internal func isEqualToString(to other: AnyObject?) -> Int8 { return _isEqual(other) } @objc(isEqual:) @_effects(readonly) final internal func isEqual(to other: AnyObject?) -> Int8 { return _isEqual(other) } @objc(copyWithZone:) final internal func copy(with zone: _SwiftNSZone?) -> AnyObject { // While __StringStorage instances aren't immutable in general, // mutations may only occur when instances are uniquely referenced. // Therefore, it is safe to return self here; any outstanding Objective-C // reference will make the instance non-unique. return self } } extension __SharedStringStorage { @objc(length) final internal var UTF16Length: Int { @_effects(readonly) get { return asString.utf16.count // UTF16View special-cases ASCII for us. } } @objc final internal var hash: UInt { @_effects(readonly) get { if isASCII { return _cocoaHashASCIIBytes(start, length: count) } return _cocoaHashString(self) } } @objc(characterAtIndex:) @_effects(readonly) final internal func character(at offset: Int) -> UInt16 { let str = asString return str.utf16[str._toUTF16Index(offset)] } @objc(getCharacters:range:) @_effects(releasenone) final internal func getCharacters( _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange ) { _getCharacters(buffer, aRange) } @objc final internal var fastestEncoding: UInt { @_effects(readonly) get { if isASCII { return _cocoaASCIIEncoding } return _cocoaUTF8Encoding } } @objc(_fastCStringContents:) @_effects(readonly) final internal func _fastCStringContents( _ requiresNulTermination: Int8 ) -> UnsafePointer? { if isASCII { return start._asCChar } return nil } @objc(UTF8String) @_effects(readonly) final internal func _utf8String() -> UnsafePointer? { return start } @objc(cStringUsingEncoding:) @_effects(readonly) final internal func cString(encoding: UInt) -> UnsafePointer? { return _cString(encoding: encoding) } @objc(getCString:maxLength:encoding:) @_effects(releasenone) final internal func getCString( _ outputPtr: UnsafeMutablePointer, maxLength: Int, encoding: UInt ) -> Int8 { return _getCString(outputPtr, maxLength, encoding) } @objc(isEqualToString:) @_effects(readonly) final internal func isEqualToString(to other: AnyObject?) -> Int8 { return _isEqual(other) } @objc(isEqual:) @_effects(readonly) final internal func isEqual(to other: AnyObject?) -> Int8 { return _isEqual(other) } @objc(copyWithZone:) final internal func copy(with zone: _SwiftNSZone?) -> AnyObject { // While __StringStorage instances aren't immutable in general, // mutations may only occur when instances are uniquely referenced. // Therefore, it is safe to return self here; any outstanding Objective-C // reference will make the instance non-unique. return self } } #endif // _runtime(_ObjC)