File: String%2BBridging.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (287 lines) | stat: -rw-r--r-- 11,580 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 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
//
//===----------------------------------------------------------------------===//

#if FOUNDATION_FRAMEWORK

@_exported import Foundation // Clang module
@_spi(Foundation) import Swift
internal import CoreFoundation_Private.CFString
internal import ObjectiveC_Private.objc_internal
internal import CoreFoundation_Private.ForFoundationOnly

//===----------------------------------------------------------------------===//
// New Strings
//===----------------------------------------------------------------------===//

//
// Conversion from NSString to Swift's native representation
//

@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
extension String {
    public init(_ cocoaString: NSString) {
        self = String._unconditionallyBridgeFromObjectiveC(cocoaString)
    }
}

@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
extension String : _ObjectiveCBridgeable {
    @_semantics("convertToObjectiveC")
    public func _bridgeToObjectiveC() -> NSString {
        // This method should not do anything extra except calling into the
        // implementation inside core.  (These two entry points should be
        // equivalent.)
        return unsafeBitCast(_bridgeToObjectiveCImpl(), to: NSString.self)
    }

    public static func _forceBridgeFromObjectiveC(_ x: NSString, result: inout String?) {
        result = String._unconditionallyBridgeFromObjectiveC(x)
    }

    public static func _conditionallyBridgeFromObjectiveC(_ x: NSString, result: inout String?) -> Bool {
        self._forceBridgeFromObjectiveC(x, result: &result)
        return result != nil
    }

    @_effects(readonly)
    public static func _unconditionallyBridgeFromObjectiveC(_ source: NSString?) -> String {
        // `nil` has historically been used as a stand-in for an empty
        // string; map it to an empty string.
        guard let source = source else {
            return ""
        }
        
#if !(arch(i386) || arch(arm) || arch(arm64_32))
        var SMALL_STRING_CAPACITY:Int { 15 }

        if OBJC_HAVE_TAGGED_POINTERS == 1 && _objc_isTaggedPointer(unsafeBitCast(source, to: UnsafeRawPointer.self)) {
            let tag = _objc_getTaggedPointerTag(unsafeBitCast(source, to: UnsafeRawPointer.self))
            if tag == OBJC_TAG_NSString {
                return String(unsafeUninitializedCapacity: SMALL_STRING_CAPACITY) {
                    _NSTaggedPointerStringGetBytes(source, $0.baseAddress!)
                }
            } else if tag == OBJC_TAG_NSAtom {
                var len = UInt16(0)
                let contentsPtr = _CFIndirectTaggedPointerStringGetContents(source, &len)
                let contents = UnsafeBufferPointer(start: contentsPtr, count: Int(len))
                // Will only fail if contents aren't valid UTF8/ASCII
                if let result = _SwiftCreateImmortalString_ForFoundation(buffer: contents, isASCII: true) {
                    return result
                }
                // Since our contents are invalid, force a real copy of the string and bridge that instead. This should basically never be hit in practice
                return source.mutableCopy() as! String
            } else if tag.rawValue == 22 /* OBJC_TAG_Foundation_1 */ {
                let cStr = source.utf8String!
                return String.init(utf8String: cStr)!
            }
        }
#endif
        
        var ascii = false
        var len = 0
        var mutable = false
        var constant = false
        
        if __CFStringIsCF(unsafeBitCast(source, to: CFString.self), &mutable, &len, &ascii, &constant) {
            if len == 0 {
                return ""
            }

            if constant {
                if ascii {
                    // We would like to use _SwiftCreateImmortalString_ForFoundation here, but we can't because we need to maintain the invariant
                    // (constantString as String as NSString) === constantString
                    // and using _SwiftCreateImmortalString_ForFoundation would make an indirect tagged string instead on the way back
                    return String(_immortalCocoaString: source, count: len, encoding: Unicode.ASCII.self)
                } else {
                    return String(_immortalCocoaString: source, count: len, encoding: Unicode.UTF16.self)
                }
            }
            
            /*
             If `source` is a mutable string, we should eagerly bridge.
             Lazy bridging will still wastefully copy it to immutable first.
             */
            if mutable {
                let eagerBridge = { (source: NSString, encoding: CFStringBuiltInEncodings, capacity: Int) -> String? in
                    let result = String(unsafeUninitializedCapacity: capacity) { buffer in
                        var usedLen = 0
                        let convertedCount = _CFNonObjCStringGetBytes(
                            unsafeBitCast(source, to: CFString.self),
                            CFRangeMake(0, len),
                            encoding.rawValue,
                            0,
                            false,
                            buffer.baseAddress.unsafelyUnwrapped,
                            capacity,
                            &usedLen
                        )
                        if convertedCount != len {
                            return 0
                        }
                        return usedLen
                    }
                    return result.isEmpty ? nil : result
                }
                if ascii {
                    if let result = eagerBridge(source, CFStringBuiltInEncodings.ASCII, len) {
                        return result
                    }
                } else {
                    if let result = eagerBridge(source, CFStringBuiltInEncodings.UTF8, source.lengthOfBytes(using: String.Encoding.utf8.rawValue)) {
                        return result
                    }
                }
            }
        } else { //not an __NSCFString
            // If `source` is a Swift-native string ("rebridging"), we should return it as-is.
            if let result = String(_nativeStorage: source) {
                return result
            }
            
            len = source.length
            if len == 0 {
                return ""
            }
        }

        /*
         Future
         • If `source` is an immutable NSCFString, we should get what String
            needs with as few calls as possible. A unified
            "get isASCII, providesFastUTF8, length" method? Is this the same
            "prepare for bridging" concept?
         • Complication: if we can prove an NSCFString has no associated objects
            we may want to eagerly bridge it. Checking that is rdar://50255938,
            but even once we have that, when to eagerly bridge vs lazily bridge
            is not necessarily an obvious set of tradeoffs.
         
         Existing stdlib entry points:
         `String(unsafeUninitializedCapacity:, initializingWith:)`
         `String(_cocoaString:)`
         `String?(_nativeStorage:)`
         `String(_immortalCocoaString:, count:, encoding:)`
         
         Should the stdlib expose
         `String(_preparedCocoaString:,isASCII:,providesFastUTF8:,length:)` for
         this code to use? Is there a better signature? (This one is just the
         one from the underlying _StringGuts() initializer)
         
         If we do all this, aside from major speedups, we can delete everything
         in _bridgeCocoaString() in the stdlib, keeping all the smarts about
         how bridging works in one place (here).
         */
        
        return String(_cocoaString: source)
    }
}

@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
extension Substring : _ObjectiveCBridgeable {
    @_semantics("convertToObjectiveC")
    public func _bridgeToObjectiveC() -> NSString {
        return String(self)._bridgeToObjectiveC()
    }

    public static func _forceBridgeFromObjectiveC(_ x: NSString, result: inout Substring?) {
        let s = String(x)
        result = s[...]
    }

    public static func _conditionallyBridgeFromObjectiveC(_ x: NSString, result: inout Substring?) -> Bool {
        self._forceBridgeFromObjectiveC(x, result: &result)
        return result != nil
    }

    @_effects(readonly)
    public static func _unconditionallyBridgeFromObjectiveC(_ source: NSString?) -> Substring {
        let str = String._unconditionallyBridgeFromObjectiveC(source)
        return str[...]
    }
}

@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
extension String: CVarArg {}

/*
 This is on NSObject so that the stdlib can call it in StringBridge.swift
 without having to synthesize a receiver (e.g. lookup a class or allocate)
 
 TODO: In the future (once the Foundation overlay can know about SmallString),
 we should move the caller of this method up into the overlay and avoid this
 indirection.
 */
private extension NSObject {
    // The ObjC selector has to start with "new" to get ARC to not autorelease
    @_effects(releasenone)
    @objc(newTaggedNSStringWithASCIIBytes_:length_:)
    func createTaggedString(bytes: UnsafePointer<UInt8>,
                            count: Int) -> AnyObject? {
        if let result = _CFStringCreateTaggedPointerString(
            bytes,
            count
        ) {
            return result.takeRetainedValue() as NSString as NSString? //just "as AnyObject" inserts unwanted bridging
        }
        return nil
    }
}

extension Substring {
    func _components(separatedBy characterSet: CharacterSet) -> [String] {
        var result = [String]()
        var searchStart = startIndex
        while searchStart < endIndex {
            let r = self[searchStart...]._rangeOfCharacter(from: characterSet, options: [])
            guard let r, !r.isEmpty else {
                break
            }

            result.append(String(self[searchStart ..< r.lowerBound]))
            searchStart = r.upperBound
        }

        result.append(String(self[searchStart..<endIndex]))

        return result
    }
}

extension BidirectionalCollection where Element == Unicode.Scalar, Index == String.Index {
    func _trimmingCharacters(in set: CharacterSet) -> SubSequence {

        var idx = startIndex
        while idx < endIndex && set.contains(self[idx]) {
            formIndex(after: &idx)
        }

        let startOfNonTrimmedRange = idx // Points at the first char not in the set
        guard startOfNonTrimmedRange != endIndex else {
            return self[endIndex...]
        }

        let beforeEnd = index(before: endIndex)
        guard startOfNonTrimmedRange < beforeEnd else {
            return self[startOfNonTrimmedRange ..< endIndex]
        }

        var backIdx = beforeEnd
        // No need to bound-check because we've already trimmed from the beginning, so we'd definitely break off of this loop before `backIdx` rewinds before `startIndex`
        while set.contains(self[backIdx]) {
            formIndex(before: &backIdx)
        }
        return self[startOfNonTrimmedRange ... backIdx]
    }

}

#endif