@@ -17,34 +17,113 @@ import SwiftShims
17
17
internal typealias _CocoaString = AnyObject
18
18
19
19
#if _runtime(_ObjC)
20
+
20
21
// Swift's String bridges NSString via this protocol and these
21
22
// variables, allowing the core stdlib to remain decoupled from
22
23
// Foundation.
23
24
25
+ @objc internal protocol _StringSelectorHolder : _NSCopying {
26
+
27
+ @objc var length : Int { get }
28
+
29
+ @objc var hash : UInt { get }
30
+
31
+ @objc ( characterAtIndex: )
32
+ func character( at offset: Int ) -> UInt16
33
+
34
+ @objc ( getCharacters: range: )
35
+ func getCharacters(
36
+ _ buffer: UnsafeMutablePointer < UInt16 > , range aRange: _SwiftNSRange
37
+ )
38
+
39
+ @objc ( _fastCStringContents: )
40
+ func _fastCStringContents(
41
+ _ requiresNulTermination: Int8
42
+ ) -> UnsafePointer < CChar > ?
43
+
44
+ @objc ( _fastCharacterContents)
45
+ func _fastCharacterContents( ) -> UnsafePointer < UInt16 > ?
46
+
47
+ @objc ( getBytes: maxLength: usedLength: encoding: options: range: remainingRange: )
48
+ func getBytes( _ buffer: UnsafeMutableRawPointer ? ,
49
+ maxLength maxBufferCount: Int ,
50
+ usedLength usedBufferCount: UnsafeMutablePointer < Int > ? ,
51
+ encoding: UInt ,
52
+ options: UInt ,
53
+ range: _SwiftNSRange ,
54
+ remaining leftover: UnsafeMutablePointer < _SwiftNSRange > ? ) -> Int8
55
+
56
+ @objc ( compare: options: range: locale: )
57
+ func compare( _ string: _CocoaString ,
58
+ options: UInt ,
59
+ range: _SwiftNSRange ,
60
+ locale: AnyObject ? ) -> Int
61
+ }
62
+
63
+ /*
64
+ Passing a _CocoaString through _objc() lets you call ObjC methods that the
65
+ compiler doesn't know about, via the protocol above. In order to get good
66
+ performance, you need a double indirection like this:
67
+
68
+ func a -> _objc -> func a'
69
+
70
+ because any refcounting @_effects on 'a' will be lost when _objc breaks ARC's
71
+ knowledge that the _CocoaString and _StringSelectorHolder are the same object.
72
+ */
73
+ @inline ( __always)
74
+ internal func _objc( _ str: _CocoaString ) -> _StringSelectorHolder {
75
+ return unsafeBitCast ( str, to: _StringSelectorHolder. self)
76
+ }
77
+
78
+ @_effects ( releasenone)
79
+ private func _copyNSString( _ str: _StringSelectorHolder ) -> _CocoaString {
80
+ return str. copy ( with: nil )
81
+ }
82
+
24
83
@usableFromInline // @testable
25
84
@_effects ( releasenone)
26
85
internal func _stdlib_binary_CFStringCreateCopy(
27
86
_ source: _CocoaString
28
87
) -> _CocoaString {
29
- let result = _swift_stdlib_CFStringCreateCopy ( nil , source) as AnyObject
30
- return result
88
+ return _copyNSString ( _objc ( source) )
89
+ }
90
+
91
+ @_effects ( readonly)
92
+ private func _NSStringLen( _ str: _StringSelectorHolder ) -> Int {
93
+ return str. length
31
94
}
32
95
33
96
@usableFromInline // @testable
34
97
@_effects ( readonly)
35
98
internal func _stdlib_binary_CFStringGetLength(
36
99
_ source: _CocoaString
37
100
) -> Int {
38
- return _swift_stdlib_CFStringGetLength ( source)
101
+ return _NSStringLen ( _objc ( source) )
102
+ }
103
+
104
+ @_effects ( readonly)
105
+ private func _NSStringCharactersPtr( _ str: _StringSelectorHolder ) -> UnsafeMutablePointer < UTF16 . CodeUnit > ? {
106
+ return UnsafeMutablePointer ( mutating: str. _fastCharacterContents ( ) )
39
107
}
40
108
41
109
@usableFromInline // @testable
42
110
@_effects ( readonly)
43
111
internal func _stdlib_binary_CFStringGetCharactersPtr(
44
112
_ source: _CocoaString
45
113
) -> UnsafeMutablePointer < UTF16 . CodeUnit > ? {
46
- return UnsafeMutablePointer (
47
- mutating: _swift_stdlib_CFStringGetCharactersPtr ( source) )
114
+ return _NSStringCharactersPtr ( _objc ( source) )
115
+ }
116
+
117
+ @_effects ( releasenone)
118
+ private func _NSStringGetCharacters(
119
+ from source: _StringSelectorHolder ,
120
+ range: Range < Int > ,
121
+ into destination: UnsafeMutablePointer < UTF16 . CodeUnit >
122
+ ) {
123
+ source. getCharacters ( destination, range: _SwiftNSRange (
124
+ location: range. startIndex,
125
+ length: range. count)
126
+ )
48
127
}
49
128
50
129
/// Copies a slice of a _CocoaString into contiguous storage of sufficient
@@ -55,68 +134,99 @@ internal func _cocoaStringCopyCharacters(
55
134
range: Range < Int > ,
56
135
into destination: UnsafeMutablePointer < UTF16 . CodeUnit >
57
136
) {
58
- _swift_stdlib_CFStringGetCharacters (
59
- source,
60
- _swift_shims_CFRange ( location: range. lowerBound, length: range. count) ,
61
- destination)
137
+ _NSStringGetCharacters ( from: _objc ( source) , range: range, into: destination)
138
+ }
139
+
140
+ @_effects ( readonly)
141
+ private func _NSStringGetCharacter(
142
+ _ target: _StringSelectorHolder , _ position: Int
143
+ ) -> UTF16 . CodeUnit {
144
+ return target. character ( at: position)
62
145
}
63
146
64
147
@_effects ( readonly)
65
148
internal func _cocoaStringSubscript(
66
149
_ target: _CocoaString , _ position: Int
67
150
) -> UTF16 . CodeUnit {
68
- let cfSelf : _swift_shims_CFStringRef = target
69
- return _swift_stdlib_CFStringGetCharacterAtIndex ( cfSelf, position)
151
+ return _NSStringGetCharacter ( _objc ( target) , position)
70
152
}
71
153
72
154
@_effects ( releasenone)
73
- internal func _cocoaStringCopyUTF8 (
74
- _ target : _CocoaString ,
155
+ private func _NSStringCopyUTF8 (
156
+ _ o : _StringSelectorHolder ,
75
157
into bufPtr: UnsafeMutableBufferPointer < UInt8 >
76
158
) -> Int ? {
77
159
let ptr = bufPtr. baseAddress. _unsafelyUnwrappedUnchecked
78
- let len = _stdlib_binary_CFStringGetLength ( target)
79
- var count = 0
80
- let converted = _swift_stdlib_CFStringGetBytes (
81
- target,
82
- _swift_shims_CFRange ( location: 0 , length: len) ,
83
- kCFStringEncodingUTF8,
84
- 0 ,
85
- 0 ,
160
+ let len = o. length
161
+ var remainingRange = _SwiftNSRange ( location: 0 , length: 0 )
162
+ var usedLen = 0
163
+ let success = 0 != o. getBytes (
86
164
ptr,
87
- bufPtr. count,
88
- & count
165
+ maxLength: bufPtr. count,
166
+ usedLength: & usedLen,
167
+ encoding: _cocoaUTF8Encoding,
168
+ options: 0 ,
169
+ range: _SwiftNSRange ( location: 0 , length: len) ,
170
+ remaining: & remainingRange
89
171
)
90
- return len == converted ? count : nil
172
+ if success && remainingRange. length == 0 {
173
+ return usedLen
174
+ }
175
+ return nil
91
176
}
92
177
93
- @_effects ( readonly )
94
- internal func _cocoaStringUTF8Count (
178
+ @_effects ( releasenone )
179
+ internal func _cocoaStringCopyUTF8 (
95
180
_ target: _CocoaString ,
181
+ into bufPtr: UnsafeMutableBufferPointer < UInt8 >
182
+ ) -> Int ? {
183
+ return _NSStringCopyUTF8 ( _objc ( target) , into: bufPtr)
184
+ }
185
+
186
+ @_effects ( readonly)
187
+ private func _NSStringUTF8Count(
188
+ _ o: _StringSelectorHolder ,
96
189
range: Range < Int >
97
190
) -> Int ? {
98
- var count = 0
99
- let len = _stdlib_binary_CFStringGetLength ( target)
100
- let converted = _swift_stdlib_CFStringGetBytes (
101
- target,
102
- _swift_shims_CFRange ( location: range. startIndex, length: range. count) ,
103
- kCFStringEncodingUTF8,
104
- 0 ,
105
- 0 ,
191
+ var remainingRange = _SwiftNSRange ( location: 0 , length: 0 )
192
+ var usedLen = 0
193
+ let success = 0 != o. getBytes (
106
194
UnsafeMutablePointer < UInt8 > ( Builtin . inttoptr_Word ( 0 . _builtinWordValue) ) ,
107
- 0 ,
108
- & count
195
+ maxLength: 0 ,
196
+ usedLength: & usedLen,
197
+ encoding: _cocoaUTF8Encoding,
198
+ options: 0 ,
199
+ range: _SwiftNSRange ( location: range. startIndex, length: range. count) ,
200
+ remaining: & remainingRange
109
201
)
110
- return converted == len ? count : nil
202
+ if success && remainingRange. length == 0 {
203
+ return usedLen
204
+ }
205
+ return nil
206
+ }
207
+
208
+ @_effects ( readonly)
209
+ internal func _cocoaStringUTF8Count(
210
+ _ target: _CocoaString ,
211
+ range: Range < Int >
212
+ ) -> Int ? {
213
+ return _NSStringUTF8Count ( _objc ( target) , range: range)
214
+ }
215
+
216
+ @_effects ( readonly)
217
+ private func _NSStringCompare(
218
+ _ o: _StringSelectorHolder , _ other: _CocoaString
219
+ ) -> Int {
220
+ let range = _SwiftNSRange ( location: 0 , length: o. length)
221
+ let options = UInt ( 2 ) /* NSLiteralSearch*/
222
+ return o. compare ( other, options: options, range: range, locale: nil )
111
223
}
112
224
113
225
@_effects ( readonly)
114
226
internal func _cocoaStringCompare(
115
227
_ string: _CocoaString , _ other: _CocoaString
116
228
) -> Int {
117
- let cfSelf : _swift_shims_CFStringRef = string
118
- let cfOther : _swift_shims_CFStringRef = other
119
- return _swift_stdlib_CFStringCompare ( cfSelf, cfOther)
229
+ return _NSStringCompare ( _objc ( string) , other)
120
230
}
121
231
122
232
@_effects ( readonly)
@@ -196,33 +306,30 @@ internal enum _KnownCocoaString {
196
306
}
197
307
198
308
#if !(arch(i386) || arch(arm))
309
+
199
310
// Resiliently write a tagged _CocoaString's contents into a buffer.
311
+ // TODO: move this to the Foundation overlay and reimplement it with
312
+ // _NSTaggedPointerStringGetBytes
200
313
@_effects ( releasenone) // @opaque
201
314
internal func _bridgeTagged(
202
315
_ cocoa: _CocoaString ,
203
316
intoUTF8 bufPtr: UnsafeMutableBufferPointer < UInt8 >
204
317
) -> Int ? {
205
318
_internalInvariant ( _isObjCTaggedPointer ( cocoa) )
206
- let ptr = bufPtr. baseAddress. _unsafelyUnwrappedUnchecked
207
- let length = _stdlib_binary_CFStringGetLength ( cocoa)
208
- _internalInvariant ( length <= _SmallString. capacity)
209
- var count = 0
210
- let numCharWritten = _swift_stdlib_CFStringGetBytes (
211
- cocoa, _swift_shims_CFRange ( location: 0 , length: length) ,
212
- kCFStringEncodingUTF8, 0 , 0 , ptr, bufPtr. count, & count)
213
- return length == numCharWritten ? count : nil
319
+ return _cocoaStringCopyUTF8 ( cocoa, into: bufPtr)
214
320
}
215
321
#endif
216
322
217
- @_effects ( releasenone) // @opaque
218
- internal func _cocoaUTF8Pointer( _ str: _CocoaString ) -> UnsafePointer < UInt8 > ? {
219
- // TODO(String bridging): Is there a better interface here? This requires nul
220
- // termination and may assume ASCII.
221
- guard let ptr = _swift_stdlib_CFStringGetCStringPtr (
222
- str, kCFStringEncodingUTF8
223
- ) else { return nil }
323
+ @_effects ( readonly)
324
+ private func _NSStringASCIIPointer( _ str: _StringSelectorHolder ) -> UnsafePointer < UInt8 > ? {
325
+ // TODO(String bridging): Is there a better interface here? Ideally we'd be
326
+ // able to ask for UTF8 rather than just ASCII
327
+ return str. _fastCStringContents ( 0 ) ? . _asUInt8
328
+ }
224
329
225
- return ptr. _asUInt8
330
+ @_effects ( readonly) // @opaque
331
+ internal func _cocoaASCIIPointer( _ str: _CocoaString ) -> UnsafePointer < UInt8 > ? {
332
+ return _NSStringASCIIPointer ( _objc ( str) )
226
333
}
227
334
228
335
private enum CocoaStringPointer {
@@ -236,11 +343,11 @@ private enum CocoaStringPointer {
236
343
private func _getCocoaStringPointer(
237
344
_ cfImmutableValue: _CocoaString
238
345
) -> CocoaStringPointer {
239
- if let utf8Ptr = _cocoaUTF8Pointer ( cfImmutableValue) {
346
+ if let asciiPtr = _cocoaASCIIPointer ( cfImmutableValue) {
240
347
// NOTE: CFStringGetCStringPointer means ASCII
241
- return . ascii( utf8Ptr )
348
+ return . ascii( asciiPtr )
242
349
}
243
- if let utf16Ptr = _swift_stdlib_CFStringGetCharactersPtr ( cfImmutableValue) {
350
+ if let utf16Ptr = _stdlib_binary_CFStringGetCharactersPtr ( cfImmutableValue) {
244
351
return . utf16( utf16Ptr)
245
352
}
246
353
return . none
0 commit comments