Skip to content

[5.0] [String] Last-minute ABI adjustment: 4-bit discriminator #21392

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 20, 2018
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
6 changes: 3 additions & 3 deletions stdlib/public/SwiftShims/CoreFoundationShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ _swift_stdlib_NSStringCStringUsingEncodingTrampoline(id _Nonnull obj,
SWIFT_RUNTIME_STDLIB_API
__swift_uint8_t
_swift_stdlib_NSStringGetCStringTrampoline(id _Nonnull obj,
_swift_shims_UInt8 *buffer,
_swift_shims_CFIndex maxLength,
unsigned long encoding);
_swift_shims_UInt8 *_Nonnull buffer,
_swift_shims_CFIndex maxLength,
unsigned long encoding);

SWIFT_RUNTIME_STDLIB_API
__swift_uintptr_t
Expand Down
65 changes: 28 additions & 37 deletions stdlib/public/core/SmallString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,9 @@ extension _SmallString {
}
}

@inlinable
internal var discriminator: _StringObject.Discriminator {
@inline(__always) get {
let value = _storage.1 &>> _StringObject.Nibbles.discriminatorShift
return _StringObject.Discriminator(UInt8(truncatingIfNeeded: value))
}
@inline(__always) set {
_storage.1 &= _StringObject.Nibbles.largeAddressMask
_storage.1 |= (
UInt64(truncatingIfNeeded: newValue._value)
&<< _StringObject.Nibbles.discriminatorShift)
}
@inlinable @inline(__always)
internal var rawDiscriminatedObject: UInt64 {
return _storage.1
}

@inlinable
Expand All @@ -96,7 +87,7 @@ extension _SmallString {
@inlinable
internal var count: Int {
@inline(__always) get {
return discriminator.smallCount
return _StringObject.getSmallCount(fromRaw: rawDiscriminatedObject)
}
}

Expand All @@ -108,27 +99,22 @@ extension _SmallString {
@inlinable
internal var isASCII: Bool {
@inline(__always) get {
return discriminator.smallIsASCII
return _StringObject.getSmallIsASCII(fromRaw: rawDiscriminatedObject)
}
}

// Give raw, nul-terminated code units. This is only for limited internal
// usage: it always clears the discriminator and count (in case it's full)
@inlinable
@inlinable @inline(__always)
internal var zeroTerminatedRawCodeUnits: RawBitPattern {
@inline(__always) get {
return (
self._storage.0,
self._storage.1 & _StringObject.Nibbles.largeAddressMask)
}
let smallStringCodeUnitMask: UInt64 = 0x00FF_FFFF_FFFF_FFFF
return (self._storage.0, self._storage.1 & smallStringCodeUnitMask)
}

@inlinable
internal func computeIsASCII() -> Bool {
// TODO(String micro-performance): Evaluate other expressions, e.g. | first
let asciiMask: UInt64 = 0x8080_8080_8080_8080
let raw = zeroTerminatedRawCodeUnits
return (raw.0 & asciiMask == 0) && (raw.1 & asciiMask == 0)
return (raw.0 | raw.1) & asciiMask == 0
}
}

Expand Down Expand Up @@ -220,7 +206,7 @@ extension _SmallString {

// Overwrite stored code units, including uninitialized. `f` should return the
// new count.
@inlinable @inline(__always)
@inline(__always)
internal mutating func withMutableCapacity(
_ f: (UnsafeMutableBufferPointer<UInt8>) throws -> Int
) rethrows {
Expand All @@ -231,14 +217,28 @@ extension _SmallString {
return try f(UnsafeMutableBufferPointer(
start: ptr, count: _SmallString.capacity))
}

_internalInvariant(len <= _SmallString.capacity)
discriminator = .small(withCount: len, isASCII: self.computeIsASCII())

let (leading, trailing) = self.zeroTerminatedRawCodeUnits
self = _SmallString(leading: leading, trailing: trailing, count: len)
}
}

// Creation
extension _SmallString {
@inlinable @inline(__always)
internal init(leading: UInt64, trailing: UInt64, count: Int) {
_internalInvariant(count <= _SmallString.capacity)

let isASCII = (leading | trailing) & 0x8080_8080_8080_8080 == 0
let countAndDiscriminator = UInt64(truncatingIfNeeded: count) &<< 56
| _StringObject.Nibbles.small(isASCII: isASCII)
_internalInvariant(trailing & countAndDiscriminator == 0)

self.init(raw: (leading, trailing | countAndDiscriminator))
_internalInvariant(self.count == count)
}

// Direct from UTF-8
@inlinable @inline(__always)
internal init?(_ input: UnsafeBufferPointer<UInt8>) {
Expand All @@ -251,11 +251,7 @@ extension _SmallString {
let leading = _bytesToUInt64(ptr, Swift.min(input.count, 8))
let trailing = count > 8 ? _bytesToUInt64(ptr + 8, count &- 8) : 0

let isASCII = (leading | trailing) & 0x8080_8080_8080_8080 == 0
let discriminator = _StringObject.Discriminator.small(
withCount: count,
isASCII: isASCII)
self.init(raw: (leading, trailing | discriminator.rawBits))
self.init(leading: leading, trailing: trailing, count: count)
}

@usableFromInline // @testable
Expand All @@ -273,13 +269,8 @@ extension _SmallString {
}
_internalInvariant(writeIdx == totalCount)

let isASCII = base.isASCII && other.isASCII
let discriminator = _StringObject.Discriminator.small(
withCount: totalCount,
isASCII: isASCII)

let (leading, trailing) = result.zeroTerminatedRawCodeUnits
self.init(raw: (leading, trailing | discriminator.rawBits))
self.init(leading: leading, trailing: trailing, count: totalCount)
}
}

Expand Down
75 changes: 36 additions & 39 deletions stdlib/public/core/StringBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 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
Expand Down Expand Up @@ -47,8 +47,8 @@ internal func _stdlib_binary_CFStringGetCharactersPtr(
mutating: _swift_stdlib_CFStringGetCharactersPtr(source))
}

/// Copies a slice of a _CocoaString into contiguous storage of
/// sufficient capacity.
/// Copies a slice of a _CocoaString into contiguous storage of sufficient
/// capacity.
@_effects(releasenone)
internal func _cocoaStringCopyCharacters(
from source: _CocoaString,
Expand Down Expand Up @@ -81,54 +81,46 @@ internal func _cocoaStringCompare(
@_effects(readonly)
internal func _cocoaHashString(
_ string: _CocoaString
) -> UInt {
) -> UInt {
return _swift_stdlib_CFStringHashNSString(string)
}

@_effects(readonly)
internal func _cocoaHashASCIIBytes(
_ bytes: UnsafePointer<UInt8>,
length: Int
) -> UInt {
_ bytes: UnsafePointer<UInt8>, length: Int
) -> UInt {
return _swift_stdlib_CFStringHashCString(bytes, length)
}

// These "trampolines" are effectively objc_msgSend_super.
// They bypass our implementations to use NSString's
// They bypass our implementations to use NSString's.

@_effects(readonly)
internal func _cocoaCStringUsingEncodingTrampoline(
_ string: _CocoaString,
_ encoding: UInt)
-> UnsafePointer<UInt8>? {
return _swift_stdlib_NSStringCStringUsingEncodingTrampoline(
string,
encoding)
_ string: _CocoaString, _ encoding: UInt
) -> UnsafePointer<UInt8>? {
return _swift_stdlib_NSStringCStringUsingEncodingTrampoline(string, encoding)
}




@_effects(releasenone)
internal func _cocoaGetCStringTrampoline(
_ string: _CocoaString,
_ buffer: UnsafeMutablePointer<UInt8>,
_ maxLength: Int,
_ encoding: UInt)
-> Int8 {
return Int8(_swift_stdlib_NSStringGetCStringTrampoline(string,
buffer,
maxLength,
encoding))
_ string: _CocoaString,
_ buffer: UnsafeMutablePointer<UInt8>,
_ maxLength: Int,
_ encoding: UInt
) -> Int8 {
return Int8(_swift_stdlib_NSStringGetCStringTrampoline(
string, buffer, maxLength, encoding))
}

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

private var kCFStringEncodingASCII : _swift_shims_CFStringEncoding {
@inline(__always) get { return 0x0600 }
}

private var kCFStringEncodingUTF8 : _swift_shims_CFStringEncoding {
@inline(__always) get { return 0x8000100 }
}
Expand All @@ -149,12 +141,12 @@ internal enum _KnownCocoaString {
@inline(__always)
init(_ str: _CocoaString) {

#if !(arch(i386) || arch(arm))
#if !(arch(i386) || arch(arm))
if _isObjCTaggedPointer(str) {
self = .tagged
return
}
#endif
#endif

switch _unsafeAddressOfCocoaStringClass(str) {
case unsafeBitCast(_StringStorage.self, to: UInt.self):
Expand All @@ -168,7 +160,7 @@ internal enum _KnownCocoaString {
}

#if !(arch(i386) || arch(arm))
// Resiliently write a tagged cocoa string's contents into a buffer
// Resiliently write a tagged _CocoaString's contents into a buffer.
@_effects(releasenone) // @opaque
internal func _bridgeTagged(
_ cocoa: _CocoaString,
Expand Down Expand Up @@ -223,32 +215,33 @@ private func _getCocoaStringPointer(
internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts {
switch _KnownCocoaString(cocoaString) {
case .storage:
return _unsafeUncheckedDowncast(cocoaString, to: _StringStorage.self).asString._guts
return _unsafeUncheckedDowncast(
cocoaString, to: _StringStorage.self).asString._guts
case .shared:
return _unsafeUncheckedDowncast(cocoaString, to: _SharedStringStorage.self).asString._guts
return _unsafeUncheckedDowncast(
cocoaString, to: _SharedStringStorage.self).asString._guts
#if !(arch(i386) || arch(arm))
case .tagged:
return _StringGuts(_SmallString(taggedCocoa: cocoaString))
return _StringGuts(_SmallString(taggedCocoa: cocoaString))
#endif
case .cocoa:
// "copy" it into a value to be sure nobody will modify behind
// our backs. In practice, when value is already immutable, this
// "Copy" it into a value to be sure nobody will modify behind
// our backs. In practice, when value is already immutable, this
// just does a retain.
//
// TODO: Only in certain circumstances should we emit this call:
// 1) If it's immutable, just retain it.
// 2) If it's mutable with no associated information, then a copy must
// happen; might as well eagerly bridge it in.
// 3) If it's mutable with associated information, must make the call
//
let immutableCopy
= _stdlib_binary_CFStringCreateCopy(cocoaString) as AnyObject

#if !(arch(i386) || arch(arm))
#if !(arch(i386) || arch(arm))
if _isObjCTaggedPointer(immutableCopy) {
return _StringGuts(_SmallString(taggedCocoa: immutableCopy))
}
#endif
#endif

let (fastUTF8, isASCII): (Bool, Bool)
switch _getCocoaStringPointer(immutableCopy) {
Expand Down Expand Up @@ -288,9 +281,13 @@ extension String {
}
}
if _guts._object.isImmortal {
// TODO: We'd rather emit a valid ObjC object statically than create a
// shared string class instance.
let gutsCountAndFlags = _guts._object._countAndFlags
return _SharedStringStorage(
immortal: _guts._object.fastUTF8.baseAddress!,
countAndFlags: _guts._object._countAndFlags)
countAndFlags: _StringObject.CountAndFlags(
sharedCount: _guts.count, isASCII: gutsCountAndFlags.isASCII))
}

_internalInvariant(_guts._object.hasObjCBridgeableObject,
Expand Down
16 changes: 6 additions & 10 deletions stdlib/public/core/StringGuts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ extension _StringGuts {
}

internal init(_ storage: _SharedStringStorage) {
// TODO(cleanup): We should probably pass whole perf flags struct around
self.init(_StringObject(storage, isASCII: false))
self.init(_StringObject(storage))
}

internal init(
Expand Down Expand Up @@ -109,18 +108,15 @@ extension _StringGuts {
@inline(__always) get { return isFastUTF8 && _object.isASCII }
}

@inlinable
internal var isNFC: Bool {
@inline(__always) get { return _object.isNFC }
}
@inline(__always)
internal var isNFC: Bool { return _object.isNFC }

@inlinable
internal var isNFCFastUTF8: Bool {
@inline(__always)
internal var isNFCFastUTF8: Bool {
// TODO(String micro-performance): Consider a dedicated bit for this
@inline(__always) get { return _object.isNFC && isFastUTF8 }
return _object.isNFC && isFastUTF8
}

@inlinable
internal var hasNativeStorage: Bool { return _object.hasNativeStorage }

internal var hasSharedStorage: Bool { return _object.hasSharedStorage }
Expand Down
Loading