Skip to content

Commit b600653

Browse files
Merge pull request #4938 from swiftwasm/release/5.7
[pull] swiftwasm-release/5.7 from release/5.7
2 parents 74173a8 + 4dda4af commit b600653

File tree

5 files changed

+126
-9
lines changed

5 files changed

+126
-9
lines changed

stdlib/public/core/SmallString.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,27 @@ extension _SmallString {
365365
}
366366
self._invariantCheck()
367367
}
368+
369+
@_effects(readonly) // @opaque
370+
internal init?(taggedASCIICocoa cocoa: AnyObject) {
371+
self.init()
372+
var success = true
373+
self.withMutableCapacity {
374+
/*
375+
For regular NSTaggedPointerStrings we will always succeed here, but
376+
tagged NSLocalizedStrings may not fit in a SmallString
377+
*/
378+
if let len = _bridgeTaggedASCII(cocoa, intoUTF8: $0) {
379+
return len
380+
}
381+
success = false
382+
return 0
383+
}
384+
if !success {
385+
return nil
386+
}
387+
self._invariantCheck()
388+
}
368389
}
369390
#endif
370391

stdlib/public/core/StringBridge.swift

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,9 @@ internal func _cocoaStringSubscript(
167167
}
168168

169169
@_effects(releasenone)
170-
private func _NSStringCopyUTF8(
170+
private func _NSStringCopyBytes(
171171
_ o: _StringSelectorHolder,
172+
encoding: UInt,
172173
into bufPtr: UnsafeMutableRawBufferPointer
173174
) -> Int? {
174175
let ptr = bufPtr.baseAddress._unsafelyUnwrappedUnchecked
@@ -179,7 +180,7 @@ private func _NSStringCopyUTF8(
179180
ptr,
180181
maxLength: bufPtr.count,
181182
usedLength: &usedLen,
182-
encoding: _cocoaUTF8Encoding,
183+
encoding: encoding,
183184
options: 0,
184185
range: _SwiftNSRange(location: 0, length: len),
185186
remaining: &remainingRange
@@ -195,7 +196,23 @@ internal func _cocoaStringCopyUTF8(
195196
_ target: _CocoaString,
196197
into bufPtr: UnsafeMutableRawBufferPointer
197198
) -> Int? {
198-
return _NSStringCopyUTF8(_objc(target), into: bufPtr)
199+
return _NSStringCopyBytes(
200+
_objc(target),
201+
encoding: _cocoaUTF8Encoding,
202+
into: bufPtr
203+
)
204+
}
205+
206+
@_effects(releasenone)
207+
internal func _cocoaStringCopyASCII(
208+
_ target: _CocoaString,
209+
into bufPtr: UnsafeMutableRawBufferPointer
210+
) -> Int? {
211+
return _NSStringCopyBytes(
212+
_objc(target),
213+
encoding: _cocoaASCIIEncoding,
214+
into: bufPtr
215+
)
199216
}
200217

201218
@_effects(readonly)
@@ -346,17 +363,30 @@ internal func _bridgeTagged(
346363
_internalInvariant(_isObjCTaggedPointer(cocoa))
347364
return _cocoaStringCopyUTF8(cocoa, into: bufPtr)
348365
}
366+
367+
@_effects(releasenone) // @opaque
368+
internal func _bridgeTaggedASCII(
369+
_ cocoa: _CocoaString,
370+
intoUTF8 bufPtr: UnsafeMutableRawBufferPointer
371+
) -> Int? {
372+
_internalInvariant(_isObjCTaggedPointer(cocoa))
373+
return _cocoaStringCopyASCII(cocoa, into: bufPtr)
374+
}
349375
#endif
350376

351377
@_effects(readonly)
352378
private func _NSStringASCIIPointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
353-
// TODO(String bridging): Is there a better interface here? Ideally we'd be
354-
// able to ask for UTF8 rather than just ASCII
355379
//TODO(String bridging): Unconditionally asking for nul-terminated contents is
356380
// overly conservative and hurts perf with some NSStrings
357381
return str._fastCStringContents(1)?._asUInt8
358382
}
359383

384+
@_effects(readonly)
385+
private func _NSStringUTF8Pointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
386+
//We don't have a way to ask for UTF8 here currently
387+
return _NSStringASCIIPointer(str)
388+
}
389+
360390
@_effects(readonly) // @opaque
361391
private func _withCocoaASCIIPointer<R>(
362392
_ str: _CocoaString,
@@ -371,7 +401,7 @@ private func _withCocoaASCIIPointer<R>(
371401
if requireStableAddress {
372402
return nil // tagged pointer strings don't support _fastCStringContents
373403
}
374-
if let smol = _SmallString(taggedCocoa: str) {
404+
if let smol = _SmallString(taggedASCIICocoa: str) {
375405
return _StringGuts(smol).withFastUTF8 {
376406
work($0.baseAddress._unsafelyUnwrappedUnchecked)
377407
}
@@ -385,6 +415,34 @@ private func _withCocoaASCIIPointer<R>(
385415
return nil
386416
}
387417

418+
@_effects(readonly) // @opaque
419+
private func _withCocoaUTF8Pointer<R>(
420+
_ str: _CocoaString,
421+
requireStableAddress: Bool,
422+
work: (UnsafePointer<UInt8>) -> R?
423+
) -> R? {
424+
#if !(arch(i386) || arch(arm) || arch(arm64_32))
425+
if _isObjCTaggedPointer(str) {
426+
if let ptr = getConstantTaggedCocoaContents(str)?.asciiContentsPointer {
427+
return work(ptr)
428+
}
429+
if requireStableAddress {
430+
return nil // tagged pointer strings don't support _fastCStringContents
431+
}
432+
if let smol = _SmallString(taggedCocoa: str) {
433+
return _StringGuts(smol).withFastUTF8 {
434+
work($0.baseAddress._unsafelyUnwrappedUnchecked)
435+
}
436+
}
437+
}
438+
#endif
439+
defer { _fixLifetime(str) }
440+
if let ptr = _NSStringUTF8Pointer(_objc(str)) {
441+
return work(ptr)
442+
}
443+
return nil
444+
}
445+
388446
@_effects(readonly) // @opaque
389447
internal func withCocoaASCIIPointer<R>(
390448
_ str: _CocoaString,
@@ -393,12 +451,26 @@ internal func withCocoaASCIIPointer<R>(
393451
return _withCocoaASCIIPointer(str, requireStableAddress: false, work: work)
394452
}
395453

454+
@_effects(readonly) // @opaque
455+
internal func withCocoaUTF8Pointer<R>(
456+
_ str: _CocoaString,
457+
work: (UnsafePointer<UInt8>) -> R?
458+
) -> R? {
459+
return _withCocoaUTF8Pointer(str, requireStableAddress: false, work: work)
460+
}
461+
396462
@_effects(readonly)
397463
internal func stableCocoaASCIIPointer(_ str: _CocoaString)
398464
-> UnsafePointer<UInt8>? {
399465
return _withCocoaASCIIPointer(str, requireStableAddress: true, work: { $0 })
400466
}
401467

468+
@_effects(readonly)
469+
internal func stableCocoaUTF8Pointer(_ str: _CocoaString)
470+
-> UnsafePointer<UInt8>? {
471+
return _withCocoaUTF8Pointer(str, requireStableAddress: true, work: { $0 })
472+
}
473+
402474
private enum CocoaStringPointer {
403475
case ascii(UnsafePointer<UInt8>)
404476
case utf8(UnsafePointer<UInt8>)

stdlib/public/core/StringObject.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ extension _StringObject {
915915
_internalInvariant(largeFastIsShared)
916916
#if _runtime(_ObjC)
917917
if largeIsCocoa {
918-
return stableCocoaASCIIPointer(cocoaObject)._unsafelyUnwrappedUnchecked
918+
return stableCocoaUTF8Pointer(cocoaObject)._unsafelyUnwrappedUnchecked
919919
}
920920
#endif
921921

stdlib/public/core/StringStorageBridge.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import SwiftShims
1414

1515
#if _runtime(_ObjC)
1616

17-
internal let _cocoaASCIIEncoding:UInt = 1 /* NSASCIIStringEncoding */
18-
internal let _cocoaUTF8Encoding:UInt = 4 /* NSUTF8StringEncoding */
17+
internal var _cocoaASCIIEncoding:UInt { 1 } /* NSASCIIStringEncoding */
18+
internal var _cocoaUTF8Encoding:UInt { 4 } /* NSUTF8StringEncoding */
1919

2020
extension String {
2121
@available(SwiftStdlib 5.6, *)

test/stdlib/NSSlowTaggedLocalizedString.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import StdlibUnittest
1414

1515
let longTaggedTests = TestSuite("NonContiguousTaggedStrings")
1616
var constant = "Send Message to different Team"
17+
//doesn't fit in a tagged pointer because of ', but does fit in a SmallString
18+
var shortNonTagged = "Don\u{2019}t Save"
1719

1820
func runEqualLongTagged() {
1921

@@ -39,5 +41,27 @@ longTaggedTests.test("EqualLongTagged") {
3941
runEqualLongTagged()
4042
}
4143

44+
longTaggedTests.test("EqualNonASCIISubsetSmall") {
45+
if #available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) {
46+
if MemoryLayout<AnyObject>.size != 8 {
47+
return //no tagged pointers
48+
}
49+
50+
var native = shortNonTagged.withUTF8 { String(decoding: $0, as: UTF8.self) }
51+
native.reserveCapacity(30) //force into non-small form so we can reverse bridge below
52+
let longTagged = NSSlowTaggedLocalizedString.createTest()!
53+
shortNonTagged.withCString {
54+
NSSlowTaggedLocalizedString.setContents($0)
55+
}
56+
defer {
57+
NSSlowTaggedLocalizedString.setContents(nil)
58+
}
59+
let reverseBridged = unsafeBitCast(native._guts._object.largeAddressBits, to: AnyObject.self)
60+
let eq = reverseBridged.isEqual(to: longTagged)
61+
expectEqual(eq, 1)
62+
_fixLifetime(native)
63+
}
64+
}
65+
4266
runAllTests()
4367

0 commit comments

Comments
 (0)