@@ -102,9 +102,20 @@ private func _NSStringLen(_ str: _StringSelectorHolder) -> Int {
102102internal func _stdlib_binary_CFStringGetLength(
103103 _ source: _CocoaString
104104) -> Int {
105+ if let len = getConstantTaggedCocoaContents ( source) ? . utf16Length {
106+ return len
107+ }
105108 return _NSStringLen ( _objc ( source) )
106109}
107110
111+ @_effects ( readonly)
112+ internal func _isNSString( _ str: AnyObject ) -> Bool {
113+ if getConstantTaggedCocoaContents ( str) != nil {
114+ return true
115+ }
116+ return _swift_stdlib_isNSString ( str) != 0
117+ }
118+
108119@_effects ( readonly)
109120private func _NSStringCharactersPtr( _ str: _StringSelectorHolder ) -> UnsafeMutablePointer < UTF16 . CodeUnit > ? {
110121 return UnsafeMutablePointer ( mutating: str. _fastCharacterContents ( ) )
@@ -288,13 +299,24 @@ internal enum _KnownCocoaString {
288299#if !(arch(i386) || arch(arm) || arch(wasm32))
289300 case tagged
290301#endif
302+ #if arch(arm64)
303+ case constantTagged
304+ #endif
291305
292306 @inline ( __always)
293307 init ( _ str: _CocoaString ) {
294308
295309#if !(arch(i386) || arch(arm))
296310 if _isObjCTaggedPointer ( str) {
311+ #if arch(arm64)
312+ if let _ = getConstantTaggedCocoaContents ( str) {
313+ self = . constantTagged
314+ } else {
315+ self = . tagged
316+ }
317+ #else
297318 self = . tagged
319+ #endif
298320 return
299321 }
300322#endif
@@ -333,8 +355,42 @@ private func _NSStringASCIIPointer(_ str: _StringSelectorHolder) -> UnsafePointe
333355}
334356
335357@_effects ( readonly) // @opaque
336- internal func _cocoaASCIIPointer( _ str: _CocoaString ) -> UnsafePointer < UInt8 > ? {
337- return _NSStringASCIIPointer ( _objc ( str) )
358+ private func _withCocoaASCIIPointer< R> (
359+ _ str: _CocoaString ,
360+ requireStableAddress: Bool ,
361+ work: ( UnsafePointer < UInt8 > ) -> R ?
362+ ) -> R ? {
363+ #if !(arch(i386) || arch(arm))
364+ if _isObjCTaggedPointer ( str) {
365+ if let ptr = getConstantTaggedCocoaContents ( str) ? . asciiContentsPointer {
366+ return work ( ptr)
367+ }
368+ if requireStableAddress {
369+ return nil // tagged pointer strings don't support _fastCStringContents
370+ }
371+ let tmp = _StringGuts ( _SmallString ( taggedCocoa: str) )
372+ return tmp. withFastUTF8 { work ( $0. baseAddress. _unsafelyUnwrappedUnchecked) }
373+ }
374+ #endif
375+ defer { _fixLifetime ( str) }
376+ if let ptr = _NSStringASCIIPointer ( _objc ( str) ) {
377+ return work ( ptr)
378+ }
379+ return nil
380+ }
381+
382+ @_effects ( readonly) // @opaque
383+ internal func withCocoaASCIIPointer< R> (
384+ _ str: _CocoaString ,
385+ work: ( UnsafePointer < UInt8 > ) -> R ?
386+ ) -> R ? {
387+ return _withCocoaASCIIPointer ( str, requireStableAddress: false , work: work)
388+ }
389+
390+ @_effects ( readonly)
391+ internal func stableCocoaASCIIPointer( _ str: _CocoaString )
392+ -> UnsafePointer < UInt8 > ? {
393+ return _withCocoaASCIIPointer ( str, requireStableAddress: true , work: { $0 } )
338394}
339395
340396private enum CocoaStringPointer {
@@ -348,16 +404,61 @@ private enum CocoaStringPointer {
348404private func _getCocoaStringPointer(
349405 _ cfImmutableValue: _CocoaString
350406) -> CocoaStringPointer {
351- if let asciiPtr = _cocoaASCIIPointer ( cfImmutableValue) {
352- // NOTE: CFStringGetCStringPointer means ASCII
353- return . ascii( asciiPtr)
407+ if let ascii = stableCocoaASCIIPointer ( cfImmutableValue) {
408+ return . ascii( ascii)
354409 }
355410 if let utf16Ptr = _stdlib_binary_CFStringGetCharactersPtr ( cfImmutableValue) {
356411 return . utf16( utf16Ptr)
357412 }
358413 return . none
359414}
360415
416+
417+ @inline ( __always)
418+ private func getConstantTaggedCocoaContents( _ cocoaString: _CocoaString ) ->
419+ ( utf16Length: Int , asciiContentsPointer: UnsafePointer < UInt8 > ? ) ? {
420+ #if !arch(arm64)
421+ return nil
422+ #else
423+
424+ guard _isObjCTaggedPointer ( cocoaString) else {
425+ return nil
426+ }
427+
428+ let taggedValue = unsafeBitCast ( cocoaString, to: UInt . self)
429+
430+ //11000000..payload..111
431+ let tagMask : UInt =
432+ 0b1111_1111_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0111
433+ let expectedValue : UInt =
434+ 0b1100_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0111
435+
436+ guard taggedValue & tagMask == expectedValue else {
437+ return nil
438+ }
439+
440+ guard _swift_stdlib_dyld_is_objc_constant_string (
441+ cocoaString as! UnsafeRawPointer
442+ ) == 1 else {
443+ return nil
444+ }
445+
446+ let payloadMask = ~ tagMask
447+ let payload = taggedValue & payloadMask
448+ let ivarPointer = UnsafePointer < _swift_shims_builtin_CFString > (
449+ bitPattern: payload
450+ ) !
451+ let length = ivarPointer. pointee. length
452+ let isUTF16Mask : UInt = 0x0000_0000_0000_0004 //CFStringFlags bit 4: isUnicode
453+ let isASCII = ivarPointer. pointee. flags & isUTF16Mask == 0
454+ let contentsPtr = ivarPointer. pointee. str
455+ return (
456+ utf16Length: Int ( length) ,
457+ asciiContentsPointer: isASCII ? contentsPtr : nil
458+ )
459+ #endif
460+ }
461+
361462@usableFromInline
362463@_effects ( releasenone) // @opaque
363464internal func _bridgeCocoaString( _ cocoaString: _CocoaString ) -> _StringGuts {
@@ -371,6 +472,16 @@ internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts {
371472#if !(arch(i386) || arch(arm))
372473 case . tagged:
373474 return _StringGuts ( _SmallString ( taggedCocoa: cocoaString) )
475+ #if arch(arm64)
476+ case . constantTagged:
477+ let taggedContents = getConstantTaggedCocoaContents ( cocoaString) !
478+ return _StringGuts (
479+ cocoa: cocoaString,
480+ providesFastUTF8: false , //TODO: if contentsPtr is UTF8 compatible, use it
481+ isASCII: taggedContents. asciiContentsPointer != nil ,
482+ length: taggedContents. utf16Length
483+ )
484+ #endif
374485#endif
375486 case . cocoa:
376487 // "Copy" it into a value to be sure nobody will modify behind
0 commit comments