Skip to content

Commit 0070243

Browse files
authored
Merge pull request #21392 from milseman/5_0_4_bit_string
[5.0] [String] Last-minute ABI adjustment: 4-bit discriminator
2 parents 0de30f0 + 16bdea9 commit 0070243

13 files changed

+660
-847
lines changed

stdlib/public/SwiftShims/CoreFoundationShims.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ _swift_stdlib_NSStringCStringUsingEncodingTrampoline(id _Nonnull obj,
134134
SWIFT_RUNTIME_STDLIB_API
135135
__swift_uint8_t
136136
_swift_stdlib_NSStringGetCStringTrampoline(id _Nonnull obj,
137-
_swift_shims_UInt8 *buffer,
138-
_swift_shims_CFIndex maxLength,
139-
unsigned long encoding);
137+
_swift_shims_UInt8 *_Nonnull buffer,
138+
_swift_shims_CFIndex maxLength,
139+
unsigned long encoding);
140140

141141
SWIFT_RUNTIME_STDLIB_API
142142
__swift_uintptr_t

stdlib/public/core/SmallString.swift

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,9 @@ extension _SmallString {
7272
}
7373
}
7474

75-
@inlinable
76-
internal var discriminator: _StringObject.Discriminator {
77-
@inline(__always) get {
78-
let value = _storage.1 &>> _StringObject.Nibbles.discriminatorShift
79-
return _StringObject.Discriminator(UInt8(truncatingIfNeeded: value))
80-
}
81-
@inline(__always) set {
82-
_storage.1 &= _StringObject.Nibbles.largeAddressMask
83-
_storage.1 |= (
84-
UInt64(truncatingIfNeeded: newValue._value)
85-
&<< _StringObject.Nibbles.discriminatorShift)
86-
}
75+
@inlinable @inline(__always)
76+
internal var rawDiscriminatedObject: UInt64 {
77+
return _storage.1
8778
}
8879

8980
@inlinable
@@ -96,7 +87,7 @@ extension _SmallString {
9687
@inlinable
9788
internal var count: Int {
9889
@inline(__always) get {
99-
return discriminator.smallCount
90+
return _StringObject.getSmallCount(fromRaw: rawDiscriminatedObject)
10091
}
10192
}
10293

@@ -108,27 +99,22 @@ extension _SmallString {
10899
@inlinable
109100
internal var isASCII: Bool {
110101
@inline(__always) get {
111-
return discriminator.smallIsASCII
102+
return _StringObject.getSmallIsASCII(fromRaw: rawDiscriminatedObject)
112103
}
113104
}
114105

115106
// Give raw, nul-terminated code units. This is only for limited internal
116107
// usage: it always clears the discriminator and count (in case it's full)
117-
@inlinable
108+
@inlinable @inline(__always)
118109
internal var zeroTerminatedRawCodeUnits: RawBitPattern {
119-
@inline(__always) get {
120-
return (
121-
self._storage.0,
122-
self._storage.1 & _StringObject.Nibbles.largeAddressMask)
123-
}
110+
let smallStringCodeUnitMask: UInt64 = 0x00FF_FFFF_FFFF_FFFF
111+
return (self._storage.0, self._storage.1 & smallStringCodeUnitMask)
124112
}
125113

126-
@inlinable
127114
internal func computeIsASCII() -> Bool {
128-
// TODO(String micro-performance): Evaluate other expressions, e.g. | first
129115
let asciiMask: UInt64 = 0x8080_8080_8080_8080
130116
let raw = zeroTerminatedRawCodeUnits
131-
return (raw.0 & asciiMask == 0) && (raw.1 & asciiMask == 0)
117+
return (raw.0 | raw.1) & asciiMask == 0
132118
}
133119
}
134120

@@ -220,7 +206,7 @@ extension _SmallString {
220206

221207
// Overwrite stored code units, including uninitialized. `f` should return the
222208
// new count.
223-
@inlinable @inline(__always)
209+
@inline(__always)
224210
internal mutating func withMutableCapacity(
225211
_ f: (UnsafeMutableBufferPointer<UInt8>) throws -> Int
226212
) rethrows {
@@ -231,14 +217,28 @@ extension _SmallString {
231217
return try f(UnsafeMutableBufferPointer(
232218
start: ptr, count: _SmallString.capacity))
233219
}
234-
235220
_internalInvariant(len <= _SmallString.capacity)
236-
discriminator = .small(withCount: len, isASCII: self.computeIsASCII())
221+
222+
let (leading, trailing) = self.zeroTerminatedRawCodeUnits
223+
self = _SmallString(leading: leading, trailing: trailing, count: len)
237224
}
238225
}
239226

240227
// Creation
241228
extension _SmallString {
229+
@inlinable @inline(__always)
230+
internal init(leading: UInt64, trailing: UInt64, count: Int) {
231+
_internalInvariant(count <= _SmallString.capacity)
232+
233+
let isASCII = (leading | trailing) & 0x8080_8080_8080_8080 == 0
234+
let countAndDiscriminator = UInt64(truncatingIfNeeded: count) &<< 56
235+
| _StringObject.Nibbles.small(isASCII: isASCII)
236+
_internalInvariant(trailing & countAndDiscriminator == 0)
237+
238+
self.init(raw: (leading, trailing | countAndDiscriminator))
239+
_internalInvariant(self.count == count)
240+
}
241+
242242
// Direct from UTF-8
243243
@inlinable @inline(__always)
244244
internal init?(_ input: UnsafeBufferPointer<UInt8>) {
@@ -251,11 +251,7 @@ extension _SmallString {
251251
let leading = _bytesToUInt64(ptr, Swift.min(input.count, 8))
252252
let trailing = count > 8 ? _bytesToUInt64(ptr + 8, count &- 8) : 0
253253

254-
let isASCII = (leading | trailing) & 0x8080_8080_8080_8080 == 0
255-
let discriminator = _StringObject.Discriminator.small(
256-
withCount: count,
257-
isASCII: isASCII)
258-
self.init(raw: (leading, trailing | discriminator.rawBits))
254+
self.init(leading: leading, trailing: trailing, count: count)
259255
}
260256

261257
@usableFromInline // @testable
@@ -273,13 +269,8 @@ extension _SmallString {
273269
}
274270
_internalInvariant(writeIdx == totalCount)
275271

276-
let isASCII = base.isASCII && other.isASCII
277-
let discriminator = _StringObject.Discriminator.small(
278-
withCount: totalCount,
279-
isASCII: isASCII)
280-
281272
let (leading, trailing) = result.zeroTerminatedRawCodeUnits
282-
self.init(raw: (leading, trailing | discriminator.rawBits))
273+
self.init(leading: leading, trailing: trailing, count: totalCount)
283274
}
284275
}
285276

stdlib/public/core/StringBridge.swift

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -47,8 +47,8 @@ internal func _stdlib_binary_CFStringGetCharactersPtr(
4747
mutating: _swift_stdlib_CFStringGetCharactersPtr(source))
4848
}
4949

50-
/// Copies a slice of a _CocoaString into contiguous storage of
51-
/// sufficient capacity.
50+
/// Copies a slice of a _CocoaString into contiguous storage of sufficient
51+
/// capacity.
5252
@_effects(releasenone)
5353
internal func _cocoaStringCopyCharacters(
5454
from source: _CocoaString,
@@ -81,54 +81,46 @@ internal func _cocoaStringCompare(
8181
@_effects(readonly)
8282
internal func _cocoaHashString(
8383
_ string: _CocoaString
84-
) -> UInt {
84+
) -> UInt {
8585
return _swift_stdlib_CFStringHashNSString(string)
8686
}
8787

8888
@_effects(readonly)
8989
internal func _cocoaHashASCIIBytes(
90-
_ bytes: UnsafePointer<UInt8>,
91-
length: Int
92-
) -> UInt {
90+
_ bytes: UnsafePointer<UInt8>, length: Int
91+
) -> UInt {
9392
return _swift_stdlib_CFStringHashCString(bytes, length)
9493
}
9594

9695
// These "trampolines" are effectively objc_msgSend_super.
97-
// They bypass our implementations to use NSString's
96+
// They bypass our implementations to use NSString's.
9897

9998
@_effects(readonly)
10099
internal func _cocoaCStringUsingEncodingTrampoline(
101-
_ string: _CocoaString,
102-
_ encoding: UInt)
103-
-> UnsafePointer<UInt8>? {
104-
return _swift_stdlib_NSStringCStringUsingEncodingTrampoline(
105-
string,
106-
encoding)
100+
_ string: _CocoaString, _ encoding: UInt
101+
) -> UnsafePointer<UInt8>? {
102+
return _swift_stdlib_NSStringCStringUsingEncodingTrampoline(string, encoding)
107103
}
108104

109-
110-
111-
112105
@_effects(releasenone)
113106
internal func _cocoaGetCStringTrampoline(
114-
_ string: _CocoaString,
115-
_ buffer: UnsafeMutablePointer<UInt8>,
116-
_ maxLength: Int,
117-
_ encoding: UInt)
118-
-> Int8 {
119-
return Int8(_swift_stdlib_NSStringGetCStringTrampoline(string,
120-
buffer,
121-
maxLength,
122-
encoding))
107+
_ string: _CocoaString,
108+
_ buffer: UnsafeMutablePointer<UInt8>,
109+
_ maxLength: Int,
110+
_ encoding: UInt
111+
) -> Int8 {
112+
return Int8(_swift_stdlib_NSStringGetCStringTrampoline(
113+
string, buffer, maxLength, encoding))
123114
}
124115

125116
//
126-
// Conversion from NSString to Swift's native representation
117+
// Conversion from NSString to Swift's native representation.
127118
//
128119

129120
private var kCFStringEncodingASCII : _swift_shims_CFStringEncoding {
130121
@inline(__always) get { return 0x0600 }
131122
}
123+
132124
private var kCFStringEncodingUTF8 : _swift_shims_CFStringEncoding {
133125
@inline(__always) get { return 0x8000100 }
134126
}
@@ -149,12 +141,12 @@ internal enum _KnownCocoaString {
149141
@inline(__always)
150142
init(_ str: _CocoaString) {
151143

152-
#if !(arch(i386) || arch(arm))
144+
#if !(arch(i386) || arch(arm))
153145
if _isObjCTaggedPointer(str) {
154146
self = .tagged
155147
return
156148
}
157-
#endif
149+
#endif
158150

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

170162
#if !(arch(i386) || arch(arm))
171-
// Resiliently write a tagged cocoa string's contents into a buffer
163+
// Resiliently write a tagged _CocoaString's contents into a buffer.
172164
@_effects(releasenone) // @opaque
173165
internal func _bridgeTagged(
174166
_ cocoa: _CocoaString,
@@ -223,32 +215,33 @@ private func _getCocoaStringPointer(
223215
internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts {
224216
switch _KnownCocoaString(cocoaString) {
225217
case .storage:
226-
return _unsafeUncheckedDowncast(cocoaString, to: _StringStorage.self).asString._guts
218+
return _unsafeUncheckedDowncast(
219+
cocoaString, to: _StringStorage.self).asString._guts
227220
case .shared:
228-
return _unsafeUncheckedDowncast(cocoaString, to: _SharedStringStorage.self).asString._guts
221+
return _unsafeUncheckedDowncast(
222+
cocoaString, to: _SharedStringStorage.self).asString._guts
229223
#if !(arch(i386) || arch(arm))
230224
case .tagged:
231-
return _StringGuts(_SmallString(taggedCocoa: cocoaString))
225+
return _StringGuts(_SmallString(taggedCocoa: cocoaString))
232226
#endif
233227
case .cocoa:
234-
// "copy" it into a value to be sure nobody will modify behind
235-
// our backs. In practice, when value is already immutable, this
228+
// "Copy" it into a value to be sure nobody will modify behind
229+
// our backs. In practice, when value is already immutable, this
236230
// just does a retain.
237231
//
238232
// TODO: Only in certain circumstances should we emit this call:
239233
// 1) If it's immutable, just retain it.
240234
// 2) If it's mutable with no associated information, then a copy must
241235
// happen; might as well eagerly bridge it in.
242236
// 3) If it's mutable with associated information, must make the call
243-
//
244237
let immutableCopy
245238
= _stdlib_binary_CFStringCreateCopy(cocoaString) as AnyObject
246239

247-
#if !(arch(i386) || arch(arm))
240+
#if !(arch(i386) || arch(arm))
248241
if _isObjCTaggedPointer(immutableCopy) {
249242
return _StringGuts(_SmallString(taggedCocoa: immutableCopy))
250243
}
251-
#endif
244+
#endif
252245

253246
let (fastUTF8, isASCII): (Bool, Bool)
254247
switch _getCocoaStringPointer(immutableCopy) {
@@ -288,9 +281,13 @@ extension String {
288281
}
289282
}
290283
if _guts._object.isImmortal {
284+
// TODO: We'd rather emit a valid ObjC object statically than create a
285+
// shared string class instance.
286+
let gutsCountAndFlags = _guts._object._countAndFlags
291287
return _SharedStringStorage(
292288
immortal: _guts._object.fastUTF8.baseAddress!,
293-
countAndFlags: _guts._object._countAndFlags)
289+
countAndFlags: _StringObject.CountAndFlags(
290+
sharedCount: _guts.count, isASCII: gutsCountAndFlags.isASCII))
294291
}
295292

296293
_internalInvariant(_guts._object.hasObjCBridgeableObject,

stdlib/public/core/StringGuts.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ extension _StringGuts {
6161
}
6262

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

6867
internal init(
@@ -109,18 +108,15 @@ extension _StringGuts {
109108
@inline(__always) get { return isFastUTF8 && _object.isASCII }
110109
}
111110

112-
@inlinable
113-
internal var isNFC: Bool {
114-
@inline(__always) get { return _object.isNFC }
115-
}
111+
@inline(__always)
112+
internal var isNFC: Bool { return _object.isNFC }
116113

117-
@inlinable
118-
internal var isNFCFastUTF8: Bool {
114+
@inline(__always)
115+
internal var isNFCFastUTF8: Bool {
119116
// TODO(String micro-performance): Consider a dedicated bit for this
120-
@inline(__always) get { return _object.isNFC && isFastUTF8 }
117+
return _object.isNFC && isFastUTF8
121118
}
122119

123-
@inlinable
124120
internal var hasNativeStorage: Bool { return _object.hasNativeStorage }
125121

126122
internal var hasSharedStorage: Bool { return _object.hasSharedStorage }

0 commit comments

Comments
 (0)