Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions stdlib/public/Darwin/Foundation/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,28 @@ extension Substring : _ObjectiveCBridgeable {
}

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)

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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear what exactly I'm claiming is not being released here, but it does cause something to not be retain-released

@objc(newTaggedNSStringWithASCIIBytes_:length_:)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This selector has to start with "new" to get swiftc to not autorelease the return value

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the trailing underscores intended to convey privateness without screwing up the new prefix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup

func createTaggedString(bytes: UnsafePointer<UInt8>,
count: Int) -> AnyObject? {
//TODO: update this to use _CFStringCreateTaggedPointerString once we can
return CFStringCreateWithBytes(
kCFAllocatorSystemDefault,
bytes,
count,
CFStringBuiltInEncodings.UTF8.rawValue,
false
) as NSString as NSString? //just "as AnyObject" inserts unwanted bridging
}
}
14 changes: 1 addition & 13 deletions stdlib/public/SwiftShims/CoreFoundationShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,17 @@ namespace swift { extern "C" {

#ifdef __OBJC2__
#if __LLP64__
typedef unsigned long long _swift_shims_CFTypeID;
typedef unsigned long long _swift_shims_CFHashCode;
typedef signed long long _swift_shims_CFIndex;
#else
typedef unsigned long _swift_shims_CFTypeID;
typedef unsigned long _swift_shims_CFHashCode;
typedef signed long _swift_shims_CFIndex;
#endif

typedef id _swift_shims_CFStringRef;
typedef __swift_uint32_t _swift_shims_CFStringEncoding;

// Consider creating SwiftMacTypes.h for these
typedef unsigned char _swift_shims_Boolean;
typedef __swift_uint8_t _swift_shims_UInt8;

SWIFT_RUNTIME_STDLIB_API
__attribute__((ns_returns_retained))
_swift_shims_CFStringRef _Nonnull _swift_stdlib_CFStringCreateWithBytes(
const void * _Nullable unused,
const __swift_uint8_t *_Nonnull bytes, _swift_shims_CFIndex numBytes,
_swift_shims_CFStringEncoding encoding,
_swift_shims_Boolean isExternalRepresentation);
typedef __swift_uint32_t _swift_shims_CFStringEncoding;

SWIFT_RUNTIME_STDLIB_API
__swift_uint8_t _swift_stdlib_isNSString(id _Nonnull obj);
Expand Down
28 changes: 21 additions & 7 deletions stdlib/public/core/StringBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ internal typealias _CocoaString = AnyObject
options: UInt,
range: _SwiftNSRange,
locale: AnyObject?) -> Int

@objc(newTaggedNSStringWithASCIIBytes_:length_:)
func createTaggedString(bytes: UnsafePointer<UInt8>,
count: Int) -> AnyObject?
}

/*
Expand Down Expand Up @@ -410,19 +414,28 @@ extension String {
}
}

@_effects(releasenone)
private func _createNSString(
_ receiver: _StringSelectorHolder,
_ ptr: UnsafePointer<UInt8>,
_ count: Int,
_ encoding: UInt32
) -> AnyObject? {
return receiver.createTaggedString(bytes: ptr, count: count)
}

@_effects(releasenone)
private func _createCFString(
_ ptr: UnsafePointer<UInt8>,
_ count: Int,
_ encoding: UInt32
) -> AnyObject {
return _swift_stdlib_CFStringCreateWithBytes(
nil, //ignored in the shim for perf reasons
) -> AnyObject? {
return _createNSString(
unsafeBitCast(__StringStorage.self as AnyClass, to: _StringSelectorHolder.self),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using '__StringStorage.self as AnyClass' instead of my original '_StringSelectorHolder.self as AnyObject' avoids a bunch of machinery that appeared to be boxing something.

Unfortunately it still emits a metadata accessor call that costs about 2/3 as much as objc_msgSend, but I haven't found a good way to avoid that yet.

ptr,
count,
kCFStringEncodingUTF8,
0
) as AnyObject
encoding
)
}

extension String {
Expand All @@ -431,13 +444,14 @@ extension String {
func _bridgeToObjectiveCImpl() -> AnyObject {
// Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
if _guts.isSmallASCII {
return _guts.asSmall.withUTF8 { bufPtr in
let maybeTagged = _guts.asSmall.withUTF8 { bufPtr in
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we switch to _CFStringCreateTaggedPointerString this will start returning nil more

return _createCFString(
bufPtr.baseAddress._unsafelyUnwrappedUnchecked,
bufPtr.count,
kCFStringEncodingUTF8
)
}
if let tagged = maybeTagged { return tagged }
}
if _guts.isSmall {
// We can't form a tagged pointer String, so grow to a non-small String,
Expand Down
25 changes: 0 additions & 25 deletions stdlib/public/stubs/FoundationHelpers.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,13 @@

using namespace swift;

template <class FromTy> struct DestType;

#define BRIDGE_TYPE(FROM, TO) \
template <> struct DestType<FROM> { using type = TO; }

BRIDGE_TYPE(_swift_shims_CFStringRef, CFStringRef);
BRIDGE_TYPE(_swift_shims_CFStringEncoding, CFStringEncoding);
BRIDGE_TYPE(CFStringRef, _swift_shims_CFStringRef);

template <class FromTy>
static typename DestType<FromTy>::type cast(FromTy value) {
return (typename DestType<FromTy>::type) value;
}

__swift_uint8_t
swift::_swift_stdlib_isNSString(id obj) {
//TODO: we can likely get a small perf win by using _NSIsNSString on
//sufficiently new OSs
return CFGetTypeID((CFTypeRef)obj) == CFStringGetTypeID() ? 1 : 0;
}

_swift_shims_CFStringRef
swift::_swift_stdlib_CFStringCreateWithBytes(
const void *unused, const uint8_t *bytes,
_swift_shims_CFIndex numBytes, _swift_shims_CFStringEncoding encoding,
_swift_shims_Boolean isExternalRepresentation) {
assert(unused == NULL);
return cast(CFStringCreateWithBytes(kCFAllocatorSystemDefault, bytes, numBytes,
cast(encoding),
isExternalRepresentation));
}

extern "C" CFHashCode CFStringHashCString(const uint8_t *bytes, CFIndex len);
extern "C" CFHashCode CFStringHashNSString(id str);

Expand Down