Merge pull request #16850 from milseman/4_2_string_inlinable_indexable
[4.2] Cherry-pick String inlinable and index changes
diff --git a/benchmark/single-source/ArrayAppend.swift b/benchmark/single-source/ArrayAppend.swift
index 8ed0959..87bc4ee 100644
--- a/benchmark/single-source/ArrayAppend.swift
+++ b/benchmark/single-source/ArrayAppend.swift
@@ -18,9 +18,11 @@
BenchmarkInfo(name: "ArrayAppend", runFunction: run_ArrayAppend, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendArrayOfInt", runFunction: run_ArrayAppendArrayOfInt, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendAscii", runFunction: run_ArrayAppendAscii, tags: [.validation, .api, .Array]),
+ BenchmarkInfo(name: "ArrayAppendAsciiSubstring", runFunction: run_ArrayAppendAsciiSubstring, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendFromGeneric", runFunction: run_ArrayAppendFromGeneric, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendGenericStructs", runFunction: run_ArrayAppendGenericStructs, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendLatin1", runFunction: run_ArrayAppendLatin1, tags: [.validation, .api, .Array]),
+ BenchmarkInfo(name: "ArrayAppendLatin1Substring", runFunction: run_ArrayAppendLatin1Substring, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendLazyMap", runFunction: run_ArrayAppendLazyMap, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendOptionals", runFunction: run_ArrayAppendOptionals, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendRepeatCol", runFunction: run_ArrayAppendRepeatCol, tags: [.validation, .api, .Array]),
@@ -30,6 +32,7 @@
BenchmarkInfo(name: "ArrayAppendToFromGeneric", runFunction: run_ArrayAppendToFromGeneric, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendToGeneric", runFunction: run_ArrayAppendToGeneric, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayAppendUTF16", runFunction: run_ArrayAppendUTF16, tags: [.validation, .api, .Array]),
+ BenchmarkInfo(name: "ArrayAppendUTF16Substring", runFunction: run_ArrayAppendUTF16Substring, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayPlusEqualArrayOfInt", runFunction: run_ArrayPlusEqualArrayOfInt, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayPlusEqualFiveElementCollection", runFunction: run_ArrayPlusEqualFiveElementCollection, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayPlusEqualSingleElementCollection", runFunction: run_ArrayPlusEqualSingleElementCollection, tags: [.validation, .api, .Array]),
@@ -310,7 +313,7 @@
for _ in 0..<10 {
var nums = [UInt8]()
for _ in 0..<10_000 {
- nums += s.utf8
+ nums += getString(s).utf8
}
}
}
@@ -324,7 +327,7 @@
for _ in 0..<10 {
var nums = [UInt8]()
for _ in 0..<10_000 {
- nums += s.utf8
+ nums += getString(s).utf8
}
}
}
@@ -338,7 +341,49 @@
for _ in 0..<10 {
var nums = [UInt8]()
for _ in 0..<10_000 {
- nums += s.utf8
+ nums += getString(s).utf8
+ }
+ }
+ }
+}
+
+// Append the utf8 elements of an ascii string to a [UInt8]
+@inline(never)
+public func run_ArrayAppendAsciiSubstring(_ N: Int) {
+ let s = "the quick brown fox jumps over the lazy dog!"[...]
+ for _ in 0..<N {
+ for _ in 0..<10 {
+ var nums = [UInt8]()
+ for _ in 0..<10_000 {
+ nums += getSubstring(s).utf8
+ }
+ }
+ }
+}
+
+// Append the utf8 elements of an ascii string to a [UInt8]
+@inline(never)
+public func run_ArrayAppendLatin1Substring(_ N: Int) {
+ let s = "the quick brown fox jumps over the lazy dog\u{00A1}"[...]
+ for _ in 0..<N {
+ for _ in 0..<10 {
+ var nums = [UInt8]()
+ for _ in 0..<10_000 {
+ nums += getSubstring(s).utf8
+ }
+ }
+ }
+}
+
+// Append the utf8 elements of an ascii string to a [UInt8]
+@inline(never)
+public func run_ArrayAppendUTF16Substring(_ N: Int) {
+ let s = "the quick brown 🦊 jumps over the lazy dog"[...]
+ for _ in 0..<N {
+ for _ in 0..<10 {
+ var nums = [UInt8]()
+ for _ in 0..<10_000 {
+ nums += getSubstring(s).utf8
}
}
}
diff --git a/benchmark/utils/TestsUtils.swift b/benchmark/utils/TestsUtils.swift
index 74c8dea..d0ddde4 100644
--- a/benchmark/utils/TestsUtils.swift
+++ b/benchmark/utils/TestsUtils.swift
@@ -194,3 +194,8 @@
// The same for String.
@inline(never)
public func getString(_ s: String) -> String { return s }
+
+// The same for Substring.
+@inline(never)
+public func getSubstring(_ s: Substring) -> Substring { return s }
+
diff --git a/stdlib/private/SwiftPrivate/IO.swift b/stdlib/private/SwiftPrivate/IO.swift
index 8f9d2d8..1a77350 100644
--- a/stdlib/private/SwiftPrivate/IO.swift
+++ b/stdlib/private/SwiftPrivate/IO.swift
@@ -26,15 +26,13 @@
public mutating func getline() -> String? {
if let newlineIndex =
_buffer[0..<_bufferUsed].index(of: UInt8(Unicode.Scalar("\n").value)) {
- let result = String._fromWellFormedUTF8CodeUnitSequence(
- _buffer[0..<newlineIndex])
+ let result = String(decoding: _buffer[0..<newlineIndex], as: UTF8.self)
_buffer.removeSubrange(0...newlineIndex)
_bufferUsed -= newlineIndex + 1
return result
}
if isEOF && _bufferUsed > 0 {
- let result = String._fromWellFormedUTF8CodeUnitSequence(
- _buffer[0..<_bufferUsed])
+ let result = String(decoding: _buffer[0..<_bufferUsed], as: UTF8.self)
_buffer.removeAll()
_bufferUsed = 0
return result
diff --git a/stdlib/public/SDK/Foundation/ExtraStringAPIs.swift b/stdlib/public/SDK/Foundation/ExtraStringAPIs.swift
index 0244b27..6f14332 100644
--- a/stdlib/public/SDK/Foundation/ExtraStringAPIs.swift
+++ b/stdlib/public/SDK/Foundation/ExtraStringAPIs.swift
@@ -16,7 +16,7 @@
@available(swift, obsoleted: 4.0)
public init(_ offset: Int) {
_precondition(offset >= 0, "Negative UTF16 index offset not allowed")
- self.init(_offset: offset)
+ self.init(encodedOffset: offset)
}
@available(swift, deprecated: 3.2)
diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift
index d410528..dc1fabc 100644
--- a/stdlib/public/core/CString.swift
+++ b/stdlib/public/core/CString.swift
@@ -43,7 +43,6 @@
/// // Prints "Caf�"
///
/// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence.
- @inlinable // FIXME(sil-serialize-all)
public init(cString: UnsafePointer<CChar>) {
self = _decodeValidCString(cString, repair: true)
}
@@ -53,7 +52,6 @@
///
/// This is identical to init(cString: UnsafePointer<CChar> but operates on an
/// unsigned sequence of bytes.
- @inlinable // FIXME(sil-serialize-all)
public init(cString: UnsafePointer<UInt8>) {
self = _decodeValidCString(cString, repair: true)
}
@@ -84,7 +82,6 @@
/// // Prints "nil"
///
/// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence.
- @inlinable // FIXME(sil-serialize-all)
public init?(validatingUTF8 cString: UnsafePointer<CChar>) {
guard let str = _decodeCString(cString, repair: false) else {
return nil
@@ -134,7 +131,8 @@
/// - Returns: A tuple with the new string and a Boolean value that indicates
/// whether any repairs were made. If `isRepairing` is `false` and an
/// ill-formed sequence is detected, this method returns `nil`.
- @inlinable // FIXME(sil-serialize-all)
+ @_specialize(where Encoding == Unicode.UTF8)
+ @_specialize(where Encoding == Unicode.UTF16)
public static func decodeCString<Encoding : _UnicodeEncoding>(
_ cString: UnsafePointer<Encoding.CodeUnit>?,
as encoding: Encoding.Type,
@@ -157,7 +155,6 @@
/// From a non-`nil` `UnsafePointer` to a null-terminated string
/// with possibly-transient lifetime, create a null-terminated array of 'C' char.
/// Returns `nil` if passed a null pointer.
-@inlinable // FIXME(sil-serialize-all)
public func _persistCString(_ p: UnsafePointer<CChar>?) -> [CChar]? {
guard let s = p else {
return nil
@@ -170,7 +167,6 @@
return result
}
-@inlinable
internal func _decodeValidCString(
_ cString: UnsafePointer<Int8>, repair: Bool
) -> String {
@@ -182,7 +178,6 @@
}
}
-@inlinable
internal func _decodeValidCString(
_ cString: UnsafePointer<UInt8>, repair: Bool
) -> String {
@@ -191,7 +186,6 @@
return String._fromWellFormedUTF8CodeUnitSequence(bufPtr, repair: repair)
}
-@inlinable
internal func _decodeCString(
_ cString: UnsafePointer<Int8>, repair: Bool
) -> String? {
@@ -203,7 +197,6 @@
}
}
-@inlinable
internal func _decodeCString(
_ cString: UnsafePointer<UInt8>, repair: Bool
) -> String? {
@@ -216,7 +209,6 @@
/// the given pointer using the specified encoding.
///
/// This internal helper takes the string length as an argument.
-@inlinable // FIXME(sil-serialize-all)
internal func _decodeCString<Encoding : _UnicodeEncoding>(
_ cString: UnsafePointer<Encoding.CodeUnit>,
as encoding: Encoding.Type, length: Int,
diff --git a/stdlib/public/core/InputStream.swift b/stdlib/public/core/InputStream.swift
index 445cd39..6c145c8 100644
--- a/stdlib/public/core/InputStream.swift
+++ b/stdlib/public/core/InputStream.swift
@@ -26,7 +26,6 @@
/// or character combinations are preserved. The default is `true`.
/// - Returns: The string of characters read from standard input. If EOF has
/// already been reached when `readLine()` is called, the result is `nil`.
-@inlinable // FIXME(sil-serialize-all)
public func readLine(strippingNewline: Bool = true) -> String? {
var linePtrVar: UnsafeMutablePointer<UInt8>?
var readBytes = swift_stdlib_readLine_stdin(&linePtrVar)
@@ -66,7 +65,7 @@
}
}
let result = String._fromUTF8CodeUnitSequence(
- UnsafeMutableBufferPointer(start: linePtr, count: readBytes),
+ UnsafeBufferPointer(start: linePtr, count: readBytes),
repair: true)!
_stdlib_free(linePtr)
return result
diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb
index 9aea52f..665385a 100644
--- a/stdlib/public/core/Integers.swift.gyb
+++ b/stdlib/public/core/Integers.swift.gyb
@@ -1827,6 +1827,10 @@
let isNegative = Self.isSigned && self < (0 as Self)
var value = magnitude
+
+ // TODO(FIXME JIRA): All current stdlib types fit in small. Use a stack
+ // buffer instead of an array on the heap.
+
var result: [UInt8] = []
while value != 0 {
let (quotient, remainder) = _quotientAndRemainder(value)
@@ -1837,7 +1841,11 @@
if isNegative {
result.append(UInt8(("-" as Unicode.Scalar).value))
}
- return String._fromASCII(result.reversed())
+
+ result.reverse()
+ return result.withUnsafeBufferPointer {
+ return String._fromASCII($0)
+ }
}
/// A textual representation of this value.
diff --git a/stdlib/public/core/SmallString.swift b/stdlib/public/core/SmallString.swift
index 6e00d7a..8828164 100644
--- a/stdlib/public/core/SmallString.swift
+++ b/stdlib/public/core/SmallString.swift
@@ -129,7 +129,7 @@
@inlinable
public // @testable
- init?<C: RandomAccessCollection>(_ codeUnits: C) where C.Element == UInt8 {
+ init?(_ codeUnits: UnsafeBufferPointer<UInt8>) {
#if arch(i386) || arch(arm)
return nil // Never form small strings on 32-bit
#else
@@ -137,13 +137,12 @@
guard count <= _SmallUTF8String.capacity else { return nil }
self.init()
self._withAllUnsafeMutableBytes { rawBufPtr in
- let bufPtr = UnsafeMutableBufferPointer(
- start: rawBufPtr.baseAddress.unsafelyUnwrapped.assumingMemoryBound(
- to: UInt8.self),
- count: rawBufPtr.count)
- var (itr, written) = codeUnits._copyContents(initializing: bufPtr)
- _sanityCheck(itr.next() == nil)
- _sanityCheck(count == written)
+ let rawDst = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked
+ memcpy_(
+ dst: rawDst.assumingMemoryBound(to: UInt8.self),
+ src: codeUnits.baseAddress._unsafelyUnwrappedUnchecked,
+ count: count
+ )
}
_sanityCheck(self.count == 0, "overwrote count early?")
self.count = count
@@ -154,6 +153,20 @@
_invariantCheck()
#endif
}
+
+ @inlinable
+ public // @testable
+ init?(_ scalar: Unicode.Scalar) {
+#if arch(i386) || arch(arm)
+ return nil // Never form small strings on 32-bit
+#else
+ // FIXME: support transcoding
+ guard scalar.value <= 0x7F else { return nil }
+ self.init()
+ self.count = 1
+ self[0] = UInt8(truncatingIfNeeded: scalar.value)
+#endif
+ }
}
//
diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift
index edead79..4401e16 100644
--- a/stdlib/public/core/String.swift
+++ b/stdlib/public/core/String.swift
@@ -625,38 +625,46 @@
}
}
+internal func _isAllASCII(_ input: UnsafeBufferPointer<UInt8>) -> Bool {
+ for byte in input {
+ guard byte <= 0x7F else { return false }
+ }
+ return true
+}
+
extension String {
- @inlinable
- static func _fromUTF8CodeUnitSequence<C : RandomAccessCollection>(
- _ input: C, repair: Bool
- ) -> String? where C.Element == UInt8 {
+ static func _fromUTF8CodeUnitSequence(
+ _ input: UnsafeBufferPointer<UInt8>, repair: Bool
+ ) -> String? {
+ if _isAllASCII(input) {
+ return _fromASCII(input)
+ }
+
if let smol = _SmallUTF8String(input) {
return String(_StringGuts(smol))
}
+
return String._fromCodeUnits(
input, encoding: UTF8.self, repairIllFormedSequences: repair)
}
- @inlinable
- static func _fromASCII<C : RandomAccessCollection>(
- _ input: C
- ) -> String where C.Element == UInt8 {
+ @usableFromInline
+ static func _fromASCII(_ input: UnsafeBufferPointer<UInt8>) -> String {
if let smol = _SmallUTF8String(input) {
return String(_StringGuts(smol))
}
let storage = _SwiftStringStorage<UInt8>.create(
capacity: input.count, count: input.count)
- var (itr, end) = input._copyContents(initializing: storage.usedBuffer)
- _sanityCheck(itr.next() == nil)
- _sanityCheck(end == storage.usedBuffer.endIndex)
+ _sanityCheck(storage.count == input.count)
+ storage.start.initialize(
+ from: input.baseAddress._unsafelyUnwrappedUnchecked, count: input.count)
return String(_StringGuts(_large: storage))
}
- @inlinable // FIXME(sil-serialize-all)
- public // FIXME: @usableFromInline, currently public because testing...
- static func _fromWellFormedUTF8CodeUnitSequence<C : RandomAccessCollection>(
- _ input: C, repair: Bool = false
- ) -> String where C.Element == UTF8.CodeUnit {
+ @usableFromInline
+ static func _fromWellFormedUTF8CodeUnitSequence(
+ _ input: UnsafeBufferPointer<UInt8>, repair: Bool = false
+ ) -> String {
return String._fromUTF8CodeUnitSequence(input, repair: repair)!
}
}
@@ -674,9 +682,7 @@
//
// TODO: All scalars are small
if scalar.value <= 0x7f {
- if let small = _SmallUTF8String(
- Unicode.UTF8.encode(scalar)._unsafelyUnwrappedUnchecked
- ) {
+ if let small = _SmallUTF8String(scalar) {
self = String(_StringGuts(small))
return
} else {
@@ -893,7 +899,6 @@
/// // Prints "Hello, friend"
///
/// - Parameter other: Another string.
- @inlinable // FIXME(sil-serialize-all)
public mutating func append(_ other: String) {
self._guts.append(other._guts)
}
@@ -911,7 +916,6 @@
// TODO(SSO): Consider small-checking version
@inlinable // FIXME(sil-serialize-all)
- public
init<CodeUnit>(_largeStorage storage: _SwiftStringStorage<CodeUnit>)
where CodeUnit : FixedWidthInteger & UnsignedInteger {
_guts = _StringGuts(_large: storage)
@@ -970,12 +974,12 @@
/// - Parameter separator: A string to insert between each of the elements
/// in this sequence. The default separator is an empty string.
/// - Returns: A single, concatenated string.
- @inlinable // FIXME(sil-serialize-all)
+ @_specialize(where Self == Array<Substring>)
+ @_specialize(where Self == Array<String>)
public func joined(separator: String = "") -> String {
return _joined(separator: separator)
}
- @inlinable // FIXME(sil-serialize-all)
internal func _joined(separator: String = "") -> String {
let separatorSize = separator._guts.count
var width = separator._guts.byteWidth
@@ -1031,7 +1035,7 @@
/// - Parameter separator: A string to insert between each of the elements
/// in this sequence. The default separator is an empty string.
/// - Returns: A single, concatenated string.
- @inlinable // FIXME(sil-serialize-all)
+ @_specialize(where Self == Array<String>)
public func joined(separator: String = "") -> String {
return _joined(separator: separator)
}
@@ -1046,7 +1050,6 @@
@_silgen_name("swift_stdlib_NSStringUppercaseString")
internal func _stdlib_NSStringUppercaseString(_ str: AnyObject) -> _CocoaString
#else
-@inlinable // FIXME(sil-serialize-all)
internal func _nativeUnicodeLowercaseString(_ str: String) -> String {
// TODO (TODO: JIRA): check for small
@@ -1077,7 +1080,6 @@
return String(_largeStorage: storage)
}
-@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal func _nativeUnicodeUppercaseString(_ str: String) -> String {
@@ -1147,7 +1149,6 @@
/// - Returns: A lowercase copy of the string.
///
/// - Complexity: O(*n*)
- @inlinable // FIXME(sil-serialize-all)
public func lowercased() -> String {
if _guts.isASCII {
var guts = _guts
@@ -1195,7 +1196,6 @@
/// - Returns: An uppercase copy of the string.
///
/// - Complexity: O(*n*)
- @inlinable // FIXME(sil-serialize-all)
public func uppercased() -> String {
if _guts.isASCII {
var guts = _guts
diff --git a/stdlib/public/core/StringCharacterView.swift b/stdlib/public/core/StringCharacterView.swift
index 6c5e87c..f95962a 100644
--- a/stdlib/public/core/StringCharacterView.swift
+++ b/stdlib/public/core/StringCharacterView.swift
@@ -221,18 +221,14 @@
/// this view's `_baseOffset`.
@inlinable // FIXME(sil-serialize-all)
internal func _toBaseIndex(_ index: Index) -> Index {
- return Index(
- encodedOffset: index.encodedOffset - _baseOffset,
- index._cache)
+ return String.Index(from: index, adjustingEncodedOffsetBy: -_baseOffset)
}
/// Translates an index in the underlying base string into a view index using
/// this view's `_baseOffset`.
@inlinable // FIXME(sil-serialize-all)
internal func _toViewIndex(_ index: Index) -> Index {
- return Index(
- encodedOffset: index.encodedOffset + _baseOffset,
- index._cache)
+ return String.Index(from: index, adjustingEncodedOffsetBy: _baseOffset)
}
/// The position of the first character in a nonempty character view.
@@ -330,7 +326,6 @@
/// to allocate.
///
/// - Complexity: O(*n*), where *n* is the capacity being reserved.
- @inlinable // FIXME(sil-serialize-all)
public mutating func reserveCapacity(_ n: Int) {
_base.reserveCapacity(n)
}
@@ -338,7 +333,6 @@
/// Appends the given character to the character view.
///
/// - Parameter c: The character to append to the character view.
- @inlinable // FIXME(sil-serialize-all)
public mutating func append(_ c: Character) {
_base.append(c)
}
@@ -368,7 +362,6 @@
///
/// - Complexity: O(*n*) if the underlying string is bridged from
/// Objective-C, where *n* is the length of the string; otherwise, O(1).
- @inlinable // FIXME(sil-serialize-all)
@available(swift, deprecated: 3.2, message:
"Please use String or Substring directly")
public subscript(bounds: Range<Index>) -> String.CharacterView {
diff --git a/stdlib/public/core/StringComparable.swift b/stdlib/public/core/StringComparable.swift
index 8a11f27..a3022c6 100644
--- a/stdlib/public/core/StringComparable.swift
+++ b/stdlib/public/core/StringComparable.swift
@@ -27,6 +27,14 @@
if left._bitwiseEqualTo(right) {
return true
}
+ if left._isSmall && right._isSmall {
+ // TODO: Ensure normality when adding UTF-8 support
+ _sanityCheck(left._isASCIIOrSmallASCII && right._isASCIIOrSmallASCII,
+ "Need to ensure normality")
+
+ // Equal small strings should be bitwise equal if ASCII
+ return false
+ }
return compare(left, to: right) == 0
}
@@ -50,6 +58,10 @@
if left._bitwiseEqualTo(right) {
return false
}
+ if left._isSmall && right._isSmall {
+ // Small strings compare lexicographically if ASCII
+ return left._smallUTF8String._compare(right._smallUTF8String) == .less
+ }
return compare(left, to: right) == -1
}
@@ -72,14 +84,14 @@
) -> Int {
defer { _fixLifetime(left) }
defer { _fixLifetime(right) }
-
+
if left.isASCII && right.isASCII {
let leftASCII = left._unmanagedASCIIView[leftRange]
let rightASCII = right._unmanagedASCIIView[rightRange]
let result = leftASCII.compareASCII(to: rightASCII)
return result
}
-
+
let leftBits = left.rawBits
let rightBits = right.rawBits
@@ -92,14 +104,14 @@
) -> Int {
defer { _fixLifetime(left) }
defer { _fixLifetime(right) }
-
+
if left.isASCII && right.isASCII {
let leftASCII = left._unmanagedASCIIView
let rightASCII = right._unmanagedASCIIView
let result = leftASCII.compareASCII(to: rightASCII)
return result
}
-
+
let leftBits = left.rawBits
let rightBits = right.rawBits
diff --git a/stdlib/public/core/StringComparison.swift b/stdlib/public/core/StringComparison.swift
index 33ea1f6..1327f56 100644
--- a/stdlib/public/core/StringComparison.swift
+++ b/stdlib/public/core/StringComparison.swift
@@ -120,25 +120,19 @@
// TODO: coalesce many of these into a protocol to simplify the code
extension _SmallUTF8String {
+ @inlinable
func _compare(_ other: _SmallUTF8String) -> _Ordering {
#if arch(i386) || arch(arm)
_conditionallyUnreachable()
#else
- if _fastPath(self.isASCII && other.isASCII) {
- // TODO: fast in-register comparison
- return self.withUnmanagedASCII { selfView in
- return other.withUnmanagedASCII { otherView in
- return _Ordering(signedNotation: selfView.compareASCII(to: otherView))
- }
- }
+ // TODO: Ensure normality when adding UTF-8 support
+ _sanityCheck(self.isASCII && other.isASCII, "Need to ensure normality")
+ if self._storage == other._storage { return .equal }
+ for i in 0..<Swift.min(self.count, other.count) {
+ if self[i] < other[i] { return .less }
+ if self[i] > other[i] { return .greater }
}
-
- // TODO: fast in-register comparison
- return self.withUnmanagedUTF16 { selfView in
- return other.withUnmanagedUTF16 { otherView in
- return selfView._compare(otherView)
- }
- }
+ return self.count < other.count ? .less : .greater
#endif // 64-bit
}
func _compare(_contiguous other: _StringGuts) -> _Ordering {
diff --git a/stdlib/public/core/StringGraphemeBreaking.swift b/stdlib/public/core/StringGraphemeBreaking.swift
index 50a3567..9a021ad 100644
--- a/stdlib/public/core/StringGraphemeBreaking.swift
+++ b/stdlib/public/core/StringGraphemeBreaking.swift
@@ -18,29 +18,10 @@
@inlinable // FIXME(sil-serialize-all)
internal var _LF: UInt8 { return 0x0a }
-extension String.Index {
- @inlinable // FIXME(sil-serialize-all)
- internal init(encodedOffset: Int, characterStride stride: Int) {
- if _slowPath(stride == 0 || stride > UInt16.max) {
- // Don't store a 0 stride for the endIndex
- // or a truncated stride for an overlong grapheme cluster.
- self.init(encodedOffset: encodedOffset)
- return
- }
- self.init(
- encodedOffset: encodedOffset,
- .character(stride: UInt16(truncatingIfNeeded: stride)))
- }
-}
-
extension _StringVariant {
@inlinable
internal func _stride(at i: String.Index) -> Int {
- if case .character(let stride) = i._cache {
- // TODO: should _fastPath the case somehow
- _sanityCheck(stride > 0)
- return Int(stride)
- }
+ if let stride = i.characterStride { return stride }
return characterStride(atOffset: i.encodedOffset)
}
diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift
index de52a0c..203fece 100644
--- a/stdlib/public/core/StringGuts.swift
+++ b/stdlib/public/core/StringGuts.swift
@@ -101,7 +101,7 @@
@inlinable
@inline(__always)
public // @testable
- mutating func isUniqueNative() -> Bool {
+ mutating func _isUniqueNative() -> Bool {
guard _isNative else { return false }
// Note that the isUnique test must be in a separate statement;
// `isNative && _isUnique` always evaluates to false in debug builds,
@@ -123,8 +123,15 @@
@inlinable
public // @testable
var isASCII: Bool {
- // FIXME: Currently used to sometimes mean contiguous ASCII
- return _object.isContiguousASCII
+ @inline(__always) get { return _object.isContiguousASCII }
+ }
+
+ @inlinable
+ internal
+ var _isASCIIOrSmallASCII: Bool {
+ @inline(__always) get {
+ return isASCII || _isSmall && _smallUTF8String.isASCII
+ }
}
@inlinable
@@ -450,7 +457,7 @@
#if _runtime(_ObjC)
extension _StringGuts {
- public // @testable
+ @usableFromInline
var _underlyingCocoaString: _CocoaString? {
if _object.isNative {
return _object.nativeRawStorage
@@ -625,6 +632,8 @@
// Use the existing buffer if possible; otherwise copy the string into a
// new buffer.
@usableFromInline
+ @_specialize(where CodeUnit == UInt8)
+ @_specialize(where CodeUnit == UInt16)
internal
func _extractNativeStorage<CodeUnit>(
of codeUnit: CodeUnit.Type = CodeUnit.self
@@ -639,8 +648,6 @@
@_specialize(where CodeUnit == UInt8)
@_specialize(where CodeUnit == UInt16)
- @_specialize(where CodeUnit == UTF16.CodeUnit)
- @inlinable
internal
func _copyToNativeStorage<CodeUnit>(
of codeUnit: CodeUnit.Type = CodeUnit.self,
@@ -656,8 +663,7 @@
return storage
}
- @inlinable
- public // @testable
+ @usableFromInline // @testable
func _extractSlice(_ range: Range<Int>) -> _StringGuts {
if range.isEmpty { return _StringGuts() }
if range == 0..<count { return self }
@@ -687,7 +693,6 @@
_large: _copyToNativeStorage(of: UTF16.CodeUnit.self, from: range))
}
- @inlinable
internal mutating func allocationParametersForMutableStorage<CodeUnit>(
of type: CodeUnit.Type,
unusedCapacity: Int
@@ -708,7 +713,7 @@
}
// We have enough space; check if it's unique and of the correct width.
if _fastPath(_object.bitWidth == CodeUnit.bitWidth) {
- if _fastPath(isUniqueNative()) {
+ if _fastPath(_isUniqueNative()) {
return nil
}
}
@@ -718,7 +723,6 @@
// Convert ourselves (if needed) to a native string with the specified storage
// parameters and call `body` on the resulting native storage.
- @inlinable
internal
mutating func withMutableStorage<CodeUnit, R>(
of type: CodeUnit.Type = CodeUnit.self,
@@ -748,7 +752,6 @@
return result
}
- @inlinable
@inline(__always)
internal
mutating func withMutableASCIIStorage<R>(
@@ -759,7 +762,6 @@
of: UInt8.self, unusedCapacity: unusedCapacity, body)
}
- @inlinable
@inline(__always)
internal
mutating func withMutableUTF16Storage<R>(
@@ -805,16 +807,19 @@
@usableFromInline
internal
var _nonStoredCount: Int {
- if _object.isSmall {
- return _object.smallUTF8Count
- }
+ @effects(readonly)
+ get {
+ if _object.isSmall {
+ return _object.smallUTF8Count
+ }
#if _runtime(_ObjC)
- _sanityCheck(_object.isCocoa)
- return _cocoaCount
+ _sanityCheck(_object.isCocoa)
+ return _cocoaCount
#else
- _sanityCheck(_object.isOpaque)
- return _opaqueCount
+ _sanityCheck(_object.isOpaque)
+ return _opaqueCount
#endif
+ }
}
@inlinable
@@ -862,7 +867,6 @@
/// Get the UTF-16 code unit stored at the specified position in this string.
@inlinable // FIXME(sil-serialize-all)
- public // @testable
func codeUnit(atCheckedOffset offset: Int) -> UTF16.CodeUnit {
if _slowPath(_isOpaque) {
return _opaqueCodeUnit(atCheckedOffset: offset)
@@ -889,7 +893,6 @@
// Copy code units from a slice of this string into a buffer.
- @inlinable // FIXME(sil-serialize-all)
internal func _copy<CodeUnit>(
range: Range<Int>,
into dest: UnsafeMutableBufferPointer<CodeUnit>)
@@ -909,7 +912,6 @@
}
}
- @usableFromInline // @opaque
internal func _opaqueCopy<CodeUnit>(
range: Range<Int>,
into dest: UnsafeMutableBufferPointer<CodeUnit>)
@@ -925,13 +927,12 @@
_asOpaque()[range]._copy(into: dest)
}
- @inlinable
- public // TODO(StringGuts): for testing
+ @usableFromInline
mutating func reserveUnusedCapacity(
_ unusedCapacity: Int,
ascii: Bool = false
) {
- if _fastPath(isUniqueNative()) {
+ if _fastPath(_isUniqueNative()) {
if _fastPath(
ascii == (_object.bitWidth == 8) &&
_object.nativeRawStorage.unusedCapacity >= unusedCapacity) {
@@ -957,10 +958,9 @@
_invariantCheck()
}
- @inlinable
- public // TODO(StringGuts): for testing
+ @usableFromInline // @testable
mutating func reserveCapacity(_ capacity: Int) {
- if _fastPath(isUniqueNative()) {
+ if _fastPath(_isUniqueNative()) {
if _fastPath(_object.nativeRawStorage.capacity >= capacity) {
return
}
@@ -988,7 +988,6 @@
_invariantCheck()
}
- @inlinable
internal
mutating func append(_ other: _UnmanagedASCIIString) {
guard other.count > 0 else { return }
@@ -1010,7 +1009,6 @@
}
}
- @inlinable
internal
mutating func append(_ other: _UnmanagedUTF16String) {
guard other.count > 0 else { return }
@@ -1019,7 +1017,6 @@
}
}
- @inlinable
internal
mutating func append(_ other: _UnmanagedOpaqueString) {
guard other.count > 0 else { return }
@@ -1028,13 +1025,13 @@
}
}
- @inlinable
internal
mutating func append<S: StringProtocol>(_ other: S) {
self.append(other._wholeString._guts, range: other._encodedOffsetRange)
}
- public // TODO(StringGuts): for testing only
+ @usableFromInline // @testable
+ internal
mutating func append(_ other: _StringGuts) {
// FIXME(TODO: JIRA): shouldn't _isEmptySingleton be sufficient?
if _isEmptySingleton || self.count == 0 && !_object.isNative {
@@ -1059,7 +1056,6 @@
}
}
- @usableFromInline // @opaque
mutating func _opaqueAppend(opaqueOther other: _StringGuts) {
if other._isSmall {
// TODO: Fix the visitation pattern for append here. For now, we funnel
@@ -1075,8 +1071,8 @@
self.append(other._asOpaque())
}
- @inlinable
- public // TODO(StringGuts): for testing only
+ @usableFromInline
+ internal
mutating func append(_ other: _StringGuts, range: Range<Int>) {
_sanityCheck(range.lowerBound >= 0 && range.upperBound <= other.count)
guard range.count > 0 else { return }
@@ -1097,7 +1093,6 @@
}
}
- @usableFromInline // @opaque
mutating func _opaqueAppend(opaqueOther other: _StringGuts, range: Range<Int>) {
if other._isSmall {
other._smallUTF8String.withUnmanagedASCII {
@@ -1115,8 +1110,8 @@
// FIXME (TODO JIRA): Appending a character onto the end of a string should
// really have a less generic implementation, then we can drop @specialize.
//
+ @usableFromInline
@_specialize(where C == Character._SmallUTF16)
- @inlinable
mutating func append<C : RandomAccessCollection>(contentsOf other: C)
where C.Element == UInt16 {
if self._isSmall {
@@ -1218,7 +1213,8 @@
_invariantCheck()
}
- public mutating func replaceSubrange<C>(
+ @usableFromInline
+ mutating func replaceSubrange<C>(
_ bounds: Range<Int>,
with newElements: C
) where C : Collection, C.Element == UTF16.CodeUnit {
@@ -1230,87 +1226,15 @@
}
}
-extension _StringGuts : Sequence {
- public typealias Element = UTF16.CodeUnit
-
- @_fixed_layout
- public struct Iterator : IteratorProtocol {
- public typealias Element = UTF16.CodeUnit
-
- @usableFromInline
- internal let _guts: _StringGuts
- @usableFromInline
- internal let _endOffset: Int
- @usableFromInline
- internal var _nextOffset: Int
- @usableFromInline
- internal var _buffer = _FixedArray16<Element>()
- @usableFromInline
- internal var _bufferIndex: Int = 0
-
- @inlinable
- internal init(_ guts: _StringGuts, range: Range<Int>) {
- self._guts = guts
- self._endOffset = range.upperBound
- self._nextOffset = range.lowerBound
- if _fastPath(!range.isEmpty) {
- _fillBuffer()
- }
- }
-
- @inlinable
- public mutating func next() -> Element? {
- if _fastPath(_bufferIndex < _buffer.count) {
- let result = _buffer[_bufferIndex]
- _bufferIndex += 1
- return result
- }
- if _nextOffset == _endOffset {
- return nil
- }
- _fillBuffer()
- _bufferIndex = 1
- return _buffer[0]
- }
-
- @usableFromInline
- @inline(never)
- internal mutating func _fillBuffer() {
- _sanityCheck(_buffer.count == 0)
- _buffer.count = Swift.min(_buffer.capacity, _endOffset - _nextOffset)
- _sanityCheck(_buffer.count > 0)
- let guts = _guts // Make a copy to prevent overlapping access to self
- _buffer.withUnsafeMutableBufferPointer { buffer in
- let range: Range<Int> = _nextOffset ..< _nextOffset + buffer.count
- guts._copy(range: range, into: buffer)
- }
- _nextOffset += _buffer.count
- }
- }
-
- @inlinable
- public func makeIterator() -> Iterator {
- return Iterator(self, range: 0..<count)
- }
-
- @inlinable
- internal func makeIterator(in range: Range<Int>) -> Iterator {
- return Iterator(self, range: range)
- }
-}
-
extension _StringGuts {
// TODO: Drop or unify with String._fromCodeUnits
- //
- @inlinable // FIXME(sil-serialize-all)
internal
- static func fromCodeUnits<Input : Sequence, Encoding : _UnicodeEncoding>(
- _ input: Input,
+ static func fromCodeUnits<Encoding : _UnicodeEncoding>(
+ _ input: UnsafeBufferPointer<Encoding.CodeUnit>,
encoding: Encoding.Type,
repairIllFormedSequences: Bool,
minimumCapacity: Int = 0
- ) -> (_StringGuts?, hadError: Bool)
- where Input.Element == Encoding.CodeUnit {
+ ) -> (_StringGuts?, hadError: Bool) {
// Determine how many UTF-16 code units we'll need
guard let (utf16Count, isASCII) = UTF16.transcodedLength(
of: input.makeIterator(),
@@ -1349,10 +1273,9 @@
extension _StringGuts {
// For testing purposes only. Might be both inefficient and too low-level.
// There should be an eventual API on String to accomplish something similar.
- public // @_testable
- static
- func _createStringFromUTF16<C: RandomAccessCollection>(_ cus: C) -> String
- where C.Element == UInt16 {
+ @usableFromInline // @_testable
+ static internal
+ func _createStringFromUTF16(_ cus: UnsafeBufferPointer<UInt16>) -> String {
let storage = _SwiftStringStorage<UTF16.CodeUnit>.create(
capacity: cus.count, count: cus.count)
_ = storage._initialize(fromCodeUnits: cus, encoding: UTF16.self)
@@ -1368,13 +1291,11 @@
/// Returns true iff `input` was found to contain invalid code units in the
/// specified encoding. If any invalid sequences are found, they are replaced
/// with REPLACEMENT CHARACTER (U+FFFD).
- @inlinable // FIXME(sil-serialize-all)
internal
- func _initialize<Input : Sequence, Encoding: _UnicodeEncoding>(
- fromCodeUnits input: Input,
+ func _initialize<Encoding: _UnicodeEncoding>(
+ fromCodeUnits input: UnsafeBufferPointer<Encoding.CodeUnit>,
encoding: Encoding.Type
- ) -> Bool
- where Input.Element == Encoding.CodeUnit {
+ ) -> Bool {
var p = self.start
let hadError = transcode(
input.makeIterator(),
diff --git a/stdlib/public/core/StringIndex.swift b/stdlib/public/core/StringIndex.swift
index dd4e624..10a1045 100644
--- a/stdlib/public/core/StringIndex.swift
+++ b/stdlib/public/core/StringIndex.swift
@@ -13,54 +13,57 @@
/// A position of a character or code unit in a string.
@_fixed_layout // FIXME(sil-serialize-all)
public struct Index {
- @usableFromInline // FIXME(sil-serialize-all)
- internal var _compoundOffset : UInt64
- @usableFromInline
- internal var _cache: _Cache
+ internal typealias _UTF8Buffer = UTF8.EncodedScalar
- internal typealias _UTF8Buffer = _ValidUTF8Buffer<UInt64>
- @_frozen // FIXME(sil-serialize-all)
+ @usableFromInline // FIXME(sil-serialize-all)
+ internal var _compoundOffset: UInt64
+
@usableFromInline
- internal enum _Cache {
- case utf16
- case utf8(buffer: _UTF8Buffer)
- case character(stride: UInt16)
- case unicodeScalar(value: Unicode.Scalar)
- }
+ internal var _utf8Buffer = _UTF8Buffer()
+
+ @usableFromInline
+ internal var _graphemeStrideCache: UInt16 = 0
}
}
/// Convenience accessors
-extension String.Index._Cache {
+extension String.Index {
@inlinable // FIXME(sil-serialize-all)
- internal var utf16: Void? {
- if case .utf16 = self { return () } else { return nil }
+ internal var utf8Buffer: String.Index._UTF8Buffer? {
+ guard !_utf8Buffer.isEmpty else { return nil }
+ return _utf8Buffer
}
+
@inlinable // FIXME(sil-serialize-all)
- internal var utf8: String.Index._UTF8Buffer? {
- if case .utf8(let r) = self { return r } else { return nil }
+ internal var characterStride: Int? {
+ guard _graphemeStrideCache > 0 else { return nil }
+ return Int(truncatingIfNeeded: _graphemeStrideCache)
}
+
+ // TODO: Probably worth carving a bit for, or maybe a isSubScalar bit...
@inlinable // FIXME(sil-serialize-all)
- internal var character: UInt16? {
- if case .character(let r) = self { return r } else { return nil }
- }
- @inlinable // FIXME(sil-serialize-all)
- internal var unicodeScalar: UnicodeScalar? {
- if case .unicodeScalar(let r) = self { return r } else { return nil }
+ internal var isUTF8: Bool {
+ return self.utf8Buffer != nil || self.transcodedOffset > 0
}
}
extension String.Index : Equatable {
+ // A combined code unit and transcoded offset, for comparison purposes
+ @inlinable // FIXME(sil-serialize-all)
+ internal var _orderingValue: UInt64 {
+ return _compoundOffset
+ }
+
@inlinable // FIXME(sil-serialize-all)
public static func == (lhs: String.Index, rhs: String.Index) -> Bool {
- return lhs._compoundOffset == rhs._compoundOffset
+ return lhs._orderingValue == rhs._orderingValue
}
}
extension String.Index : Comparable {
@inlinable // FIXME(sil-serialize-all)
public static func < (lhs: String.Index, rhs: String.Index) -> Bool {
- return lhs._compoundOffset < rhs._compoundOffset
+ return lhs._orderingValue < rhs._orderingValue
}
}
@@ -72,56 +75,67 @@
/// of this instance.
@inlinable // FIXME(sil-serialize-all)
public func hash(into hasher: inout Hasher) {
- hasher.combine(_compoundOffset)
+ hasher.combine(_orderingValue)
}
}
extension String.Index {
- internal typealias _Self = String.Index
-
+ @inline(__always)
+ @inlinable
+ internal init(encodedOffset: Int, transcodedOffset: Int) {
+ let cuOffset = UInt64(truncatingIfNeeded: encodedOffset)
+ _sanityCheck(
+ cuOffset & 0xFFFF_0000_0000_0000 == 0, "String length capped at 48bits")
+ let transOffset = UInt64(truncatingIfNeeded: transcodedOffset)
+ _sanityCheck(transOffset <= 4, "UTF-8 max transcoding is 4 code units")
+
+ self._compoundOffset = cuOffset &<< 2 | transOffset
+ }
+
+ @inline(__always)
+ @inlinable
+ internal init(from other: String.Index, adjustingEncodedOffsetBy adj: Int) {
+ self.init(
+ encodedOffset: other.encodedOffset &+ adj,
+ transcodedOffset: other.transcodedOffset)
+ self._utf8Buffer = other._utf8Buffer
+ self._graphemeStrideCache = other._graphemeStrideCache
+ }
+
/// Creates a new index at the specified UTF-16 offset.
///
/// - Parameter offset: An offset in UTF-16 code units.
@inlinable // FIXME(sil-serialize-all)
public init(encodedOffset offset: Int) {
- _compoundOffset = UInt64(offset << _Self._strideBits)
- _cache = .utf16
+ self.init(encodedOffset: offset, transcodedOffset: 0)
}
@inlinable // FIXME(sil-serialize-all)
- internal init(encodedOffset o: Int, transcodedOffset: Int = 0, _ c: _Cache) {
- _compoundOffset = UInt64(o << _Self._strideBits | transcodedOffset)
- _cache = c
+ internal init(
+ encodedOffset offset: Int, transcodedOffset: Int, buffer: _UTF8Buffer
+ ) {
+ self.init(encodedOffset: offset, transcodedOffset: transcodedOffset)
+ self._utf8Buffer = buffer
}
-
- @inlinable // FIXME(sil-serialize-all)
- internal static var _strideBits : Int { return 2 }
- @inlinable // FIXME(sil-serialize-all)
- internal static var _mask : UInt64 { return (1 &<< _Self._strideBits) &- 1 }
-
- @inlinable // FIXME(sil-serialize-all)
- internal mutating func _setEncodedOffset(_ x: Int) {
- _compoundOffset = UInt64(x << _Self._strideBits)
+
+ @inlinable
+ internal init(encodedOffset: Int, characterStride: Int) {
+ self.init(encodedOffset: encodedOffset, transcodedOffset: 0)
+ if characterStride < UInt16.max {
+ self._graphemeStrideCache = UInt16(truncatingIfNeeded: characterStride)
+ }
}
-
+
/// The offset into a string's UTF-16 encoding for this index.
@inlinable // FIXME(sil-serialize-all)
public var encodedOffset : Int {
- return Int(_compoundOffset >> _Self._strideBits)
+ return Int(truncatingIfNeeded: _compoundOffset &>> 2)
}
/// The offset of this index within whatever encoding this is being viewed as
@inlinable // FIXME(sil-serialize-all)
- internal var _transcodedOffset : Int {
- get {
- return Int(_compoundOffset & _Self._mask)
- }
- set {
- let extended = UInt64(newValue)
- _sanityCheck(extended <= _Self._mask)
- _compoundOffset &= ~_Self._mask
- _compoundOffset |= extended
- }
+ internal var transcodedOffset: Int {
+ return Int(truncatingIfNeeded: _compoundOffset & 0x3)
}
}
@@ -130,27 +144,27 @@
@inlinable // FIXME(sil-serialize-all)
@available(swift, deprecated: 3.2)
@available(swift, obsoleted: 4.0)
- public // SPI(Foundation)
+ public // SPI(Foundation)
init(_position: Int) {
self.init(encodedOffset: _position)
}
-
+
@inlinable // FIXME(sil-serialize-all)
@available(swift, deprecated: 3.2)
@available(swift, obsoleted: 4.0)
- public // SPI(Foundation)
- init(_offset: Int) {
- self.init(encodedOffset: _offset)
+ public // SPI(Foundation)
+ init(_codeUnitOffset: Int) {
+ self.init(encodedOffset: _codeUnitOffset)
}
-
+
@inlinable // FIXME(sil-serialize-all)
@available(swift, deprecated: 3.2)
@available(swift, obsoleted: 4.0)
- public // SPI(Foundation)
+ public // SPI(Foundation)
init(_base: String.Index, in c: String.CharacterView) {
self = _base
}
-
+
/// The integer offset of this index in UTF-16 code units.
@inlinable // FIXME(sil-serialize-all)
@available(swift, deprecated: 3.2)
@@ -171,7 +185,7 @@
}
-// backward compatibility for index interchange.
+// backward compatibility for index interchange.
extension Optional where Wrapped == String.Index {
@inlinable // FIXME(sil-serialize-all)
@available(
diff --git a/stdlib/public/core/StringObject.swift b/stdlib/public/core/StringObject.swift
index ef2f5e1..5220f7d 100644
--- a/stdlib/public/core/StringObject.swift
+++ b/stdlib/public/core/StringObject.swift
@@ -709,7 +709,6 @@
}
@inlinable
- public // @testable
var isSingleByte: Bool {
@inline(__always)
get {
@@ -729,7 +728,6 @@
}
@inlinable
- public // @testable
var byteWidth: Int {
@inline(__always)
get { return isSingleByte ? 1 : 2 }
@@ -742,14 +740,12 @@
}
@inlinable
- public // @testable
var isContiguousASCII: Bool {
@inline(__always)
get { return isContiguous && isSingleByte }
}
@inlinable
- public // @testable
var isContiguousUTF16: Bool {
@inline(__always)
get { return isContiguous && !isSingleByte }
@@ -770,7 +766,6 @@
}
@inlinable
- public // @testable
var nativeRawStorage: _SwiftRawStringStorage {
@inline(__always) get {
_sanityCheck(isNative)
diff --git a/stdlib/public/core/StringRangeReplaceableCollection.swift b/stdlib/public/core/StringRangeReplaceableCollection.swift
index 40f5efb..000ac08 100644
--- a/stdlib/public/core/StringRangeReplaceableCollection.swift
+++ b/stdlib/public/core/StringRangeReplaceableCollection.swift
@@ -309,7 +309,6 @@
/// to allocate.
///
/// - Complexity: O(*n*)
- @inlinable // FIXME(sil-serialize-all)
public mutating func reserveCapacity(_ n: Int) {
_guts.reserveCapacity(n)
}
@@ -324,7 +323,6 @@
/// // Prints "Globe 🌍"
///
/// - Parameter c: The character to append to the string.
- @inlinable // FIXME(sil-serialize-all)
public mutating func append(_ c: Character) {
if let small = c._smallUTF16 {
_guts.append(contentsOf: small)
@@ -334,12 +332,10 @@
}
}
- @inlinable // FIXME(sil-serialize-all)
public mutating func append(contentsOf newElements: String) {
append(newElements)
}
- @inlinable // FIXME(sil-serialize-all)
public mutating func append(contentsOf newElements: Substring) {
_guts.append(
newElements._wholeString._guts,
@@ -459,11 +455,8 @@
@inlinable // FIXME(sil-serialize-all)
internal func _stride(of i: Index) -> Int {
- if case .character(let stride) = i._cache {
- // TODO: should _fastPath the case somehow
- _sanityCheck(stride > 0)
- return Int(stride)
- }
+ if let stride = i.characterStride { return stride }
+
let offset = i.encodedOffset
return _visitGuts(_guts, args: offset,
ascii: { ascii, offset in
diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift
index 3c19b0c..5684407 100644
--- a/stdlib/public/core/StringStorage.swift
+++ b/stdlib/public/core/StringStorage.swift
@@ -13,14 +13,14 @@
import SwiftShims
@_fixed_layout
-public
+@usableFromInline
class _SwiftRawStringStorage : _SwiftNativeNSString {
@nonobjc
- public // @testable
+ @usableFromInline
final var capacity: Int
@nonobjc
- public // @testable
+ @usableFromInline
final var count: Int
@nonobjc
@@ -36,7 +36,6 @@
@inlinable
@nonobjc
- public // @testable
final var unusedCapacity: Int {
_sanityCheck(capacity >= count)
return capacity - count
@@ -47,13 +46,16 @@
internal typealias _UTF16StringStorage = _SwiftStringStorage<UTF16.CodeUnit>
@_fixed_layout
-public final class _SwiftStringStorage<CodeUnit>
+@usableFromInline
+final class _SwiftStringStorage<CodeUnit>
: _SwiftRawStringStorage, _NSStringCore
where CodeUnit : UnsignedInteger & FixedWidthInteger {
/// Create uninitialized storage of at least the specified capacity.
- @inlinable
+ @usableFromInline
@nonobjc
+ @_specialize(where CodeUnit == UInt8)
+ @_specialize(where CodeUnit == UInt16)
internal static func create(
capacity: Int,
count: Int = 0
@@ -93,24 +95,28 @@
// NSString API
@objc(initWithCoder:)
- public convenience init(coder aDecoder: AnyObject) {
+ @usableFromInline
+ convenience init(coder aDecoder: AnyObject) {
_sanityCheckFailure("init(coder:) not implemented for _SwiftStringStorage")
}
@objc(length)
- public var length: Int {
+ @usableFromInline
+ var length: Int {
return count
}
@objc(characterAtIndex:)
- public func character(at index: Int) -> UInt16 {
+ @usableFromInline
+ func character(at index: Int) -> UInt16 {
defer { _fixLifetime(self) }
precondition(index >= 0 && index < count, "Index out of bounds")
return UInt16(start[index])
}
@objc(getCharacters:range:)
- public func getCharacters(
+ @usableFromInline
+ func getCharacters(
_ buffer: UnsafeMutablePointer<UInt16>,
range aRange: _SwiftNSRange
) {
@@ -128,13 +134,15 @@
}
@objc(_fastCharacterContents)
- public func _fastCharacterContents() -> UnsafePointer<UInt16>? {
+ @usableFromInline
+ func _fastCharacterContents() -> UnsafePointer<UInt16>? {
guard CodeUnit.self == UInt16.self else { return nil }
return UnsafePointer(rawStart.assumingMemoryBound(to: UInt16.self))
}
@objc(copyWithZone:)
- public func copy(with zone: _SwiftNSZone?) -> AnyObject {
+ @usableFromInline
+ func copy(with zone: _SwiftNSZone?) -> AnyObject {
// While _SwiftStringStorage instances aren't immutable in general,
// mutations may only occur when instances are uniquely referenced.
// Therefore, it is safe to return self here; any outstanding Objective-C
@@ -190,7 +198,6 @@
extension _SwiftStringStorage {
// Append operations
- @inlinable
@nonobjc
internal final func _appendInPlace<OtherCodeUnit>(
_ other: _UnmanagedString<OtherCodeUnit>
@@ -202,7 +209,6 @@
self.count += otherCount
}
- @inlinable
@nonobjc
internal final func _appendInPlace(_ other: _UnmanagedOpaqueString) {
let otherCount = Int(other.count)
@@ -211,7 +217,6 @@
self.count += otherCount
}
- @inlinable
@nonobjc
internal final func _appendInPlace<C: Collection>(contentsOf other: C)
where C.Element == CodeUnit {
@@ -224,7 +229,6 @@
count += otherCount
}
- @inlinable
@_specialize(where C == Character._SmallUTF16, CodeUnit == UInt8)
@nonobjc
internal final func _appendInPlaceUTF16<C: Collection>(contentsOf other: C)
@@ -242,7 +246,6 @@
}
extension _SwiftStringStorage {
- @inlinable
@nonobjc
internal final func _appendInPlace(_ other: _StringGuts, range: Range<Int>) {
if _slowPath(other._isOpaque) {
@@ -273,7 +276,6 @@
_appendInPlace(other._asOpaque()[range])
}
- @inlinable
@nonobjc
internal final func _appendInPlace(_ other: _StringGuts) {
if _slowPath(other._isOpaque) {
@@ -302,13 +304,11 @@
_appendInPlace(other._asOpaque())
}
- @inlinable
@nonobjc
internal final func _appendInPlace(_ other: String) {
self._appendInPlace(other._guts)
}
- @inlinable
@nonobjc
internal final func _appendInPlace<S : StringProtocol>(_ other: S) {
self._appendInPlace(
diff --git a/stdlib/public/core/StringUTF16.swift b/stdlib/public/core/StringUTF16.swift
index e99d7b7..b89880b 100644
--- a/stdlib/public/core/StringUTF16.swift
+++ b/stdlib/public/core/StringUTF16.swift
@@ -265,7 +265,6 @@
self._guts = _guts
}
- @inlinable // FIXME(sil-serialize-all)
public var description: String {
return String(_guts._extractSlice(_encodedOffsetRange))
}
@@ -323,9 +322,9 @@
// there is no owner and elements are dropped from the end.
let wholeString = String(utf16._guts)
guard
- let start = UTF16Index(_offset: utf16._offset)
+ let start = UTF16Index(encodedOffset: utf16._offset)
.samePosition(in: wholeString),
- let end = UTF16Index(_offset: utf16._offset + utf16._length)
+ let end = UTF16Index(encodedOffset: utf16._offset + utf16._length)
.samePosition(in: wholeString)
else
{
@@ -393,7 +392,7 @@
public init?(
_ sourcePosition: String.Index, within target: String.UTF16View
) {
- guard sourcePosition._transcodedOffset == 0 else { return nil }
+ guard sourcePosition.transcodedOffset == 0 else { return nil }
self.init(encodedOffset: sourcePosition.encodedOffset)
}
diff --git a/stdlib/public/core/StringUTF8.swift b/stdlib/public/core/StringUTF8.swift
index 02a0c16..ecd789c 100644
--- a/stdlib/public/core/StringUTF8.swift
+++ b/stdlib/public/core/StringUTF8.swift
@@ -134,8 +134,15 @@
/// If the UTF-8 view is empty, `startIndex` is equal to `endIndex`.
@inlinable // FIXME(sil-serialize-all)
public var startIndex: Index {
- let r = _index(atEncodedOffset: _guts.startIndex)
- if _legacyOffsets.start == 0 { return r }
+ let r: Index
+ if _fastPath(_guts._isASCIIOrSmallASCII) {
+ r = Index(encodedOffset: 0)
+ } else {
+ r = _nonASCIIIndex(atEncodedOffset: 0)
+ }
+ _sanityCheck(r.encodedOffset == 0)
+ if _fastPath(_legacyOffsets.start == 0) { return r }
+
return index(r, offsetBy: numericCast(_legacyOffsets.start))
}
@@ -160,32 +167,55 @@
}
}
- @inlinable // FIXME(sil-serialize-all)
- internal func _index(atEncodedOffset n: Int) -> Index {
- if _fastPath(_guts.isASCII) { return Index(encodedOffset: n) }
+ @inline(never)
+ @effects(releasenone)
+ @usableFromInline
+ internal func _nonASCIIIndex(atEncodedOffset n: Int) -> Index {
+ _sanityCheck(!_guts._isASCIIOrSmallASCII)
let count = _guts.count
if n == count { return endIndex }
+ let buffer: Index._UTF8Buffer = _visitGuts(
+ _guts, range: (n..<count, performBoundsCheck: true),
+ ascii: { _ in
+ Builtin.unreachable()
+ return Index._UTF8Buffer() },
+ utf16: { utf16 in
+ var i = utf16.makeIterator()
+ return UTF8View._fillBuffer(from: &i) },
+ opaque: { opaque in
+ var i = opaque.makeIterator()
+ return UTF8View._fillBuffer(from: &i)}
+ )
+ return Index(encodedOffset: n, transcodedOffset: 0, buffer: buffer)
+ }
+
+ @inline(__always)
+ internal
+ static func _fillBuffer<Iter: IteratorProtocol>(
+ from i: inout Iter
+ ) -> Index._UTF8Buffer where Iter.Element == UInt16 {
var p = UTF16.ForwardParser()
- var i = _guts.makeIterator(in: n..<count)
var buffer = Index._UTF8Buffer()
- Loop:
while true {
switch p.parseScalar(from: &i) {
case .valid(let u16):
let u8 = Unicode.UTF8.transcode(u16, from: Unicode.UTF16.self)
._unsafelyUnwrappedUnchecked
- if buffer.count + u8.count > buffer.capacity { break Loop }
+ if buffer.count + u8.count > buffer.capacity {
+ return buffer
+ }
buffer.append(contentsOf: u8)
case .error:
let u8 = Unicode.UTF8.encodedReplacementCharacter
- if buffer.count + u8.count > buffer.capacity { break Loop }
+ if buffer.count + u8.count > buffer.capacity {
+ return buffer
+ }
buffer.append(contentsOf: u8)
case .emptyInput:
- break Loop
+ return buffer
}
}
- return Index(encodedOffset: n, .utf8(buffer: buffer))
}
/// Returns the next consecutive position after `i`.
@@ -194,20 +224,29 @@
@inlinable // FIXME(sil-serialize-all)
@inline(__always)
public func index(after i: Index) -> Index {
- if _fastPath(_guts.isASCII) {
+ if _fastPath(_guts._isASCIIOrSmallASCII) {
precondition(i.encodedOffset < _guts.count)
return Index(encodedOffset: i.encodedOffset + 1)
}
+ return _nonASCIIIndex(after: i)
+ }
+
+ @inline(never)
+ @effects(releasenone)
+ @usableFromInline
+ internal func _nonASCIIIndex(after i: Index) -> Index {
+ _sanityCheck(!_guts._isASCIIOrSmallASCII)
+
var j = i
// Ensure j's cache is utf8
- if _slowPath(j._cache.utf8 == nil) {
- j = _index(atEncodedOffset: j.encodedOffset)
+ if _slowPath(j.utf8Buffer == nil) {
+ j = _nonASCIIIndex(atEncodedOffset: j.encodedOffset)
precondition(j != endIndex, "Index out of bounds")
}
- let buffer = j._cache.utf8._unsafelyUnwrappedUnchecked
+ let buffer = j.utf8Buffer._unsafelyUnwrappedUnchecked
var scalarLength16 = 1
let b0 = buffer.first._unsafelyUnwrappedUnchecked
@@ -219,13 +258,13 @@
}
else {
// Number of bytes consumed in this scalar
- let n8 = j._transcodedOffset + 1
+ let n8 = j.transcodedOffset + 1
// If we haven't reached a scalar boundary...
if _fastPath(n8 < leading1s) {
// Advance to the next position in this scalar
return Index(
encodedOffset: j.encodedOffset,
- transcodedOffset: n8, .utf8(buffer: buffer))
+ transcodedOffset: n8, buffer: buffer)
}
// We reached a scalar boundary; compute the underlying utf16's width
// based on the number of utf8 code units
@@ -236,24 +275,34 @@
if _fastPath(!nextBuffer.isEmpty) {
return Index(
encodedOffset: j.encodedOffset + scalarLength16,
- .utf8(buffer: nextBuffer))
+ transcodedOffset: 0,
+ buffer: nextBuffer)
}
// If nothing left in the buffer, refill it.
- return _index(atEncodedOffset: j.encodedOffset + scalarLength16)
+ return _nonASCIIIndex(atEncodedOffset: j.encodedOffset + scalarLength16)
}
@inlinable // FIXME(sil-serialize-all)
public func index(before i: Index) -> Index {
- if _fastPath(_guts.isASCII) {
+ if _fastPath(_guts._isASCIIOrSmallASCII) {
precondition(i.encodedOffset > 0)
return Index(encodedOffset: i.encodedOffset - 1)
}
- if i._transcodedOffset != 0 {
- _sanityCheck(i._cache.utf8 != nil)
- var r = i
- r._compoundOffset = r._compoundOffset &- 1
- return r
+ return _nonASCIIIndex(before: i)
+ }
+
+ @inline(never)
+ @effects(releasenone)
+ @usableFromInline
+ internal func _nonASCIIIndex(before i: Index) -> Index {
+ _sanityCheck(!_guts._isASCIIOrSmallASCII)
+ if i.transcodedOffset != 0 {
+ _sanityCheck(i.utf8Buffer != nil)
+ return Index(
+ encodedOffset: i.encodedOffset,
+ transcodedOffset: i.transcodedOffset &- 1,
+ buffer: i.utf8Buffer._unsafelyUnwrappedUnchecked)
}
// Handle the scalar boundary the same way as the not-a-utf8-index case.
@@ -265,25 +314,34 @@
return Index(
encodedOffset: i.encodedOffset &- (u8.count < 4 ? 1 : 2),
transcodedOffset: u8.count &- 1,
- .utf8(buffer: String.Index._UTF8Buffer(u8))
- )
+ buffer: String.Index._UTF8Buffer(u8))
}
@inlinable // FIXME(sil-serialize-all)
public func distance(from i: Index, to j: Index) -> Int {
- if _fastPath(_guts.isASCII) {
+ if _fastPath(_guts._isASCIIOrSmallASCII) {
return j.encodedOffset - i.encodedOffset
}
- return j >= i
- ? _forwardDistance(from: i, to: j) : -_forwardDistance(from: j, to: i)
+ return _nonASCIIDistance(from: i, to: j)
}
- @inlinable // FIXME(sil-serialize-all)
- @inline(__always)
- internal func _forwardDistance(from i: Index, to j: Index) -> Int {
- return j._transcodedOffset - i._transcodedOffset +
- String.UTF8View._count(fromUTF16: IteratorSequence(_guts.makeIterator(
- in: i.encodedOffset..<j.encodedOffset)))
+ @inline(never)
+ @effects(releasenone)
+ @usableFromInline
+ internal func _nonASCIIDistance(from i: Index, to j: Index) -> Int {
+ let forwards = j >= i
+
+ let start, end: Index
+ if forwards {
+ start = i
+ end = j
+ } else {
+ start = j
+ end = i
+ }
+ let countAbs = end.transcodedOffset - start.transcodedOffset
+ + _gutsNonASCIIUTF8Count(start.encodedOffset..<end.encodedOffset)
+ return forwards ? countAbs : -countAbs
}
/// Accesses the code unit at the given position.
@@ -302,22 +360,34 @@
public subscript(position: Index) -> UTF8.CodeUnit {
@inline(__always)
get {
- if _fastPath(_guts.isASCII) {
- let ascii = _guts._unmanagedASCIIView
+ if _fastPath(_guts._isASCIIOrSmallASCII) {
let offset = position.encodedOffset
- _precondition(offset < ascii.count, "Index out of bounds")
- return ascii.buffer[position.encodedOffset]
- }
- var j = position
- while true {
- if case .utf8(let buffer) = j._cache {
- _onFastPath()
- return buffer[
- buffer.index(buffer.startIndex, offsetBy: j._transcodedOffset)]
+ _precondition(offset < _guts.count, "Index out of bounds")
+
+ if _guts._isSmall {
+ return _guts._smallUTF8String[offset]
}
- j = _index(atEncodedOffset: j.encodedOffset)
- precondition(j < endIndex, "Index out of bounds")
+ return _guts._unmanagedASCIIView.buffer[offset]
}
+
+ return _nonASCIISubscript(position: position)
+ }
+ }
+
+ @inline(never)
+ @effects(releasenone)
+ @usableFromInline
+ internal func _nonASCIISubscript(position: Index) -> UTF8.CodeUnit {
+ _sanityCheck(!_guts._isASCIIOrSmallASCII)
+ var j = position
+ while true {
+ if let buffer = j.utf8Buffer {
+ _onFastPath()
+ return buffer[
+ buffer.index(buffer.startIndex, offsetBy: j.transcodedOffset)]
+ }
+ j = _nonASCIIIndex(atEncodedOffset: j.encodedOffset)
+ precondition(j < endIndex, "Index out of bounds")
}
}
@@ -357,7 +427,6 @@
/// print(strlen(ptr.baseAddress!))
/// }
/// // Prints "6"
- @inlinable // FIXME(sil-serialize-all)
public var utf8CString: ContiguousArray<CChar> {
var result = ContiguousArray<CChar>()
result.reserveCapacity(utf8.count + 1)
@@ -400,14 +469,13 @@
/// slice of the `picnicGuest.utf8` view.
///
/// - Parameter utf8: A UTF-8 code sequence.
- @inlinable // FIXME(sil-serialize-all)
@available(swift, deprecated: 3.2,
message: "Failable initializer was removed in Swift 4. When upgrading to Swift 4, please use non-failable String.init(_:UTF8View)")
@available(swift, obsoleted: 4.0,
message: "Please use non-failable String.init(_:UTF8View) instead")
public init?(_ utf8: UTF8View) {
- if utf8.startIndex._transcodedOffset != 0
- || utf8.endIndex._transcodedOffset != 0
+ if utf8.startIndex.transcodedOffset != 0
+ || utf8.endIndex.transcodedOffset != 0
|| utf8._legacyPartialCharacters.start
|| utf8._legacyPartialCharacters.end {
return nil
@@ -547,21 +615,26 @@
}
}
+// Used to calculate a running count. For non-BMP scalars, it's important if the
+// prior code unit was a leading surrogate (validity).
+internal func _utf8Count(_ utf16CU: UInt16, prev: UInt16) -> Int {
+ switch utf16CU {
+ case 0..<0x80: return 1
+ case 0x80..<0x800: return 2
+ case 0x800..<0xDC00: return 3
+ case 0xDC00..<0xE000: return UTF16.isLeadSurrogate(prev) ? 1 : 3
+ default: return 3
+ }
+}
+
extension String.UTF8View {
- @inlinable // FIXME(sil-serialize-all)
- internal static func _count<Source: Sequence>(fromUTF16 source: Source) -> Int
- where Source.Element == Unicode.UTF16.CodeUnit
- {
+ internal static func _count<Source: RandomAccessCollection>(
+ fromUTF16 source: Source
+ ) -> Int where Source.Element == Unicode.UTF16.CodeUnit {
var result = 0
var prev: Unicode.UTF16.CodeUnit = 0
for u in source {
- switch u {
- case 0..<0x80: result += 1
- case 0x80..<0x800: result += 2
- case 0x800..<0xDC00: result += 3
- case 0xDC00..<0xE000: result += UTF16.isLeadSurrogate(prev) ? 1 : 3
- default: result += 3
- }
+ result += _utf8Count(u, prev: prev)
prev = u
}
return result
@@ -569,11 +642,23 @@
@inlinable // FIXME(sil-serialize-all)
public var count: Int {
- if _fastPath(_guts.isASCII) { return _guts.count }
- return _visitGuts(_guts,
+ let gutsCount = _guts.count
+ if _fastPath(_guts._isASCIIOrSmallASCII) { return gutsCount }
+ return _gutsNonASCIIUTF8Count(0..<gutsCount)
+ }
+
+ @inline(never)
+ @effects(releasenone)
+ @usableFromInline
+ internal func _gutsNonASCIIUTF8Count(
+ _ range: Range<Int>
+ ) -> Int {
+ _sanityCheck(!_guts._isASCIIOrSmallASCII)
+ return _visitGuts(_guts, range: (range, performBoundsCheck: true),
ascii: { ascii in return ascii.count },
utf16: { utf16 in return String.UTF8View._count(fromUTF16: utf16) },
- opaque: { opaque in return String.UTF8View._count(fromUTF16: opaque) })
+ opaque: { opaque in return String.UTF8View._count(fromUTF16: opaque) }
+ )
}
}
@@ -617,17 +702,14 @@
/// - sourcePosition: A position in a `String` or one of its views.
/// - target: The `UTF8View` in which to find the new position.
@inlinable // FIXME(sil-serialize-all)
- public init?(_ sourcePosition: String.Index, within target: String.UTF8View) {
- switch sourcePosition._cache {
- case .utf8:
- self.init(encodedOffset: sourcePosition.encodedOffset,
- transcodedOffset:sourcePosition._transcodedOffset, sourcePosition._cache)
-
- default:
- guard String.UnicodeScalarView(target._guts)._isOnUnicodeScalarBoundary(
- sourcePosition) else { return nil }
- self.init(encodedOffset: sourcePosition.encodedOffset)
+ public init?(_ idx: String.Index, within target: String.UTF8View) {
+ guard idx.isUTF8 ||
+ String.UnicodeScalarView(target._guts)._isOnUnicodeScalarBoundary(idx)
+ else {
+ return nil
}
+
+ self = idx
}
}
@@ -698,7 +780,6 @@
return String.UTF8View.SubSequence(self, _bounds: r)
}
- @inlinable // FIXME(sil-serialize-all)
@available(swift, obsoleted: 4)
public subscript(r: Range<Index>) -> String.UTF8View {
let wholeString = String(_guts)
@@ -710,27 +791,26 @@
r.upperBound.encodedOffset == _guts.count) ||
r.upperBound.samePosition(in: wholeString) == nil)
- if r.upperBound._transcodedOffset == 0 {
+ if r.upperBound.transcodedOffset == 0 {
return String.UTF8View(
_guts._extractSlice(
r.lowerBound.encodedOffset..<r.upperBound.encodedOffset),
- legacyOffsets: (r.lowerBound._transcodedOffset, 0),
+ legacyOffsets: (r.lowerBound.transcodedOffset, 0),
legacyPartialCharacters: legacyPartialCharacters)
}
- let b0 = r.upperBound._cache.utf8!.first!
+ let b0 = r.upperBound.utf8Buffer!.first!
let scalarLength8 = (~b0).leadingZeroBitCount
let scalarLength16 = scalarLength8 == 4 ? 2 : 1
let coreEnd = r.upperBound.encodedOffset + scalarLength16
return String.UTF8View(
_guts._extractSlice(r.lowerBound.encodedOffset..<coreEnd),
legacyOffsets: (
- r.lowerBound._transcodedOffset,
- r.upperBound._transcodedOffset - scalarLength8),
+ r.lowerBound.transcodedOffset,
+ r.upperBound.transcodedOffset - scalarLength8),
legacyPartialCharacters: legacyPartialCharacters)
}
- @inlinable // FIXME(sil-serialize-all)
@available(swift, obsoleted: 4)
public subscript(bounds: ClosedRange<Index>) -> String.UTF8View {
return self[bounds.relative(to: self)]
diff --git a/stdlib/public/core/StringUnicodeScalarView.swift b/stdlib/public/core/StringUnicodeScalarView.swift
index 18c7e52..d0e4203 100644
--- a/stdlib/public/core/StringUnicodeScalarView.swift
+++ b/stdlib/public/core/StringUnicodeScalarView.swift
@@ -264,7 +264,6 @@
return String(_guts)
}
- @inlinable // FIXME(sil-serialize-all)
public var debugDescription: String {
return "StringUnicodeScalarView(\(self.description.debugDescription))"
}
@@ -369,7 +368,6 @@
/// to allocate.
///
/// - Complexity: O(*n*), where *n* is the capacity being reserved.
- @inlinable // FIXME(sil-serialize-all)
public mutating func reserveCapacity(_ n: Int) {
_guts.reserveCapacity(n)
}
@@ -377,7 +375,6 @@
/// Appends the given Unicode scalar to the view.
///
/// - Parameter c: The character to append to the string.
- @inlinable // FIXME(sil-serialize-all)
public mutating func append(_ c: Unicode.Scalar) {
if _fastPath(_guts.isASCII && c.value <= 0x7f) {
_guts.withMutableASCIIStorage(unusedCapacity: 1) { storage in
@@ -407,7 +404,6 @@
/// - Parameter newElements: A sequence of Unicode scalar values.
///
/// - Complexity: O(*n*), where *n* is the length of the resulting view.
- @inlinable // FIXME(sil-serialize-all)
public mutating func append<S : Sequence>(contentsOf newElements: S)
where S.Element == Unicode.Scalar {
// FIXME: Keep ASCII storage if possible
@@ -451,7 +447,6 @@
/// `newElements`. If the call to `replaceSubrange(_:with:)` simply
/// removes elements at the end of the string, the complexity is O(*n*),
/// where *n* is equal to `bounds.count`.
- @inlinable // FIXME(sil-serialize-all)
public mutating func replaceSubrange<C>(
_ bounds: Range<Index>,
with newElements: C
@@ -532,7 +527,7 @@
if i == startIndex || i == endIndex {
return true
}
- if i._transcodedOffset != 0 { return false }
+ if i.transcodedOffset != 0 { return false }
let i2 = _toCoreIndex(i)
if _fastPath(!UTF16.isTrailSurrogate(_guts[i2])) { return true }
return i2 == 0 || !UTF16.isLeadSurrogate(_guts[i2 &- 1])
@@ -632,7 +627,6 @@
///
/// - Complexity: O(*n*) if the underlying string is bridged from
/// Objective-C, where *n* is the length of the string; otherwise, O(1).
- @inlinable // FIXME(sil-serialize-all)
@available(swift, obsoleted: 4)
public subscript(r: Range<Index>) -> String.UnicodeScalarView {
let rawSubRange: Range<Int> =
diff --git a/stdlib/public/core/Substring.swift.gyb b/stdlib/public/core/Substring.swift.gyb
index b619c94..bf0b85c 100644
--- a/stdlib/public/core/Substring.swift.gyb
+++ b/stdlib/public/core/Substring.swift.gyb
@@ -18,7 +18,6 @@
/// instance.
///
/// - Complexity: O(*n*), where *n* is the length of `substring`.
- @inlinable // FIXME(sil-serialize-all)
public init(_ substring: Substring) {
let wholeGuts = substring._wholeString._guts
self.init(wholeGuts._extractSlice(substring._encodedOffsetRange))
@@ -371,7 +370,7 @@
% View = ViewPrefix + 'View'
% _View = '_CharacterView' if property == 'characters' else View
% _property = '_characters' if property == 'characters' else property
-%
+%
extension Substring {
% if View == 'CharacterView':
@available(swift, deprecated: 3.2, message:
@@ -387,7 +386,10 @@
}
extension Substring.${_View} : BidirectionalCollection {
- public typealias Index = String.Index
+ public typealias Index = String.${View}.Index
+ public typealias Indices = String.${View}.Indices
+ public typealias Element = String.${View}.Element
+ public typealias SubSequence = Substring.${_View}
/// Creates an instance that slices `base` at `_bounds`.
@inlinable // FIXME(sil-serialize-all)
@@ -408,22 +410,66 @@
return startIndex.encodedOffset..<endIndex.encodedOffset
}
+ //
+ // Plumb slice operations through
+ //
@inlinable // FIXME(sil-serialize-all)
public var startIndex: Index { return _slice.startIndex }
+
@inlinable // FIXME(sil-serialize-all)
public var endIndex: Index { return _slice.endIndex }
+
@inlinable // FIXME(sil-serialize-all)
- public func index(after i: Index) -> Index {
- return _slice.index(after: i)
- }
+ public subscript(index: Index) -> Element { return _slice[index] }
+
@inlinable // FIXME(sil-serialize-all)
- public func index(before i: Index) -> Index {
- return _slice.index(before: i)
- }
+ public var indices: Indices { return _slice.indices }
+
@inlinable // FIXME(sil-serialize-all)
- public subscript(i: Index) -> String.${View}.Element {
- return _slice[i]
+ public func index(after i: Index) -> Index { return _slice.index(after: i) }
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func formIndex(after i: inout Index) {
+ _slice.formIndex(after: &i)
}
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func index(_ i: Index, offsetBy n: Int) -> Index {
+ return _slice.index(i, offsetBy: n)
+ }
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func index(
+ _ i: Index, offsetBy n: Int, limitedBy limit: Index
+ ) -> Index? {
+ return _slice.index(i, offsetBy: n, limitedBy: limit)
+ }
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func distance(from start: Index, to end: Index) -> Int {
+ return _slice.distance(from: start, to: end)
+ }
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func _failEarlyRangeCheck(_ index: Index, bounds: Range<Index>) {
+ _slice._failEarlyRangeCheck(index, bounds: bounds)
+ }
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func _failEarlyRangeCheck(
+ _ range: Range<Index>, bounds: Range<Index>
+ ) {
+ _slice._failEarlyRangeCheck(range, bounds: bounds)
+ }
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func index(before i: Index) -> Index { return _slice.index(before: i) }
+
+ @inlinable // FIXME(sil-serialize-all)
+ public func formIndex(before i: inout Index) {
+ _slice.formIndex(before: &i)
+ }
+
@inlinable // FIXME(sil-serialize-all)
% if property == 'characters':
@available(swift, deprecated: 3.2, message:
@@ -529,7 +575,7 @@
self.init(String(elements))
}
}
-
+
@inlinable // FIXME(sil-serialize-all)
public mutating func append<S : Sequence>(contentsOf elements: S)
where S.Element == Character {
@@ -550,7 +596,7 @@
public func uppercased() -> String {
return String(self).uppercased()
}
-
+
@inlinable // FIXME(sil-serialize-all)
public func filter(
_ isIncluded: (Element) throws -> Bool
diff --git a/stdlib/public/core/UnicodeParser.swift b/stdlib/public/core/UnicodeParser.swift
index 8fba40f..444a633 100644
--- a/stdlib/public/core/UnicodeParser.swift
+++ b/stdlib/public/core/UnicodeParser.swift
@@ -133,51 +133,3 @@
}
}
}
-
-/*
-extension Unicode {
- @_fixed_layout
- @usableFromInline
- internal struct _TranscodingIterator<
- SourceCodeUnits : IteratorProtocol,
- Parser : Unicode.Parser,
- TargetEncoding : Unicode.Encoding
- > where Parser.Encoding.CodeUnit == SourceCodeUnits.Element {
-
- @inline(__always)
- @inlinable
- public init(source: SourceCodeUnits, parser: Parser) {
- _scalars = _ParsingIterator(codeUnits: source, parser: parser)
- let firstScalar_ = _scalars.next()
- if _fastPath(firstScalar_ != nil), let firstScalar = firstScalar_ {
- _codeUnits = TargetEncoding._transcode(
- firstScalar, from: Parser.Encoding.self).makeIterator()
- }
- else {
- _codeUnits = TargetEncoding._encode(
- UnicodeScalar(_unchecked: 0)).makeIterator()
- while _codeUnits.next() != nil { }
- return
- }
- }
-
- internal var _scalars: _ParsingIterator<SourceCodeUnits, Parser>
- internal var _codeUnits: TargetEncoding.EncodedScalar.Iterator
- }
-}
-
-
-extension Unicode._TranscodingIterator : IteratorProtocol, Sequence {
- @inline(__always)
- @inlinable
- mutating public func next() -> TargetEncoding.CodeUnit? {
- if let x = _codeUnits.next() { return x }
- let nextScalar_ = _scalars.next()
- if _fastPath(nextScalar_ != nil), let nextScalar = nextScalar_ {
- _codeUnits = TargetEncoding._transcode(
- nextScalar, from: Parser.Encoding.self).makeIterator()
- }
- return _codeUnits.next()
- }
-}
-*/
diff --git a/stdlib/public/core/UnmanagedString.swift b/stdlib/public/core/UnmanagedString.swift
index d109c17..df7eaa2 100644
--- a/stdlib/public/core/UnmanagedString.swift
+++ b/stdlib/public/core/UnmanagedString.swift
@@ -55,6 +55,23 @@
}
}
+@inlinable
+@inline(__always)
+internal
+func memcpy_<
+ Source: FixedWidthInteger & UnsignedInteger
+>(
+ dst: UnsafeMutablePointer<Source>, src: UnsafePointer<Source>, count: Int
+) {
+ // Don't use the for-in-range syntax to avoid precondition checking in Range.
+ // This enables vectorization of the memcpy loop.
+ var i = 0
+ while i < count {
+ dst[i] = src[i]
+ i = i &+ 1
+ }
+}
+
@_fixed_layout
@usableFromInline
internal
diff --git a/test/Sema/fixed_ambiguities/rdar35623181.swift b/test/Sema/fixed_ambiguities/rdar35623181.swift
index 39dbf53..834b8d9 100644
--- a/test/Sema/fixed_ambiguities/rdar35623181.swift
+++ b/test/Sema/fixed_ambiguities/rdar35623181.swift
@@ -1,6 +1,8 @@
-
// RUN: %target-swift-frontend -emit-sil -verify %s | %FileCheck %s
+// XFAIL: *
+// ^ SR-7673
+
extension Sequence where Element == String {
func record() -> String {
// CHECK: function_ref @$Ss20LazySequenceProtocolPsE3mapys0a3MapB0Vy8ElementsQzqd__Gqd__7ElementQzclF : $@convention(method) <τ_0_0 where τ_0_0 : LazySequenceProtocol><τ_1_0> (@guaranteed @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @out τ_1_0, @in_guaranteed τ_0_0) -> @out LazyMapSequence<τ_0_0.Elements, τ_1_0
diff --git a/test/stdlib/SmallString.swift b/test/stdlib/SmallString.swift
index 664542b..32512e5 100644
--- a/test/stdlib/SmallString.swift
+++ b/test/stdlib/SmallString.swift
@@ -41,6 +41,26 @@
}
}
+// Testing helper inits
+extension _SmallUTF8String {
+ init?(_ codeUnits: Array<UInt8>) {
+ guard let smol = codeUnits.withUnsafeBufferPointer({
+ return _SmallUTF8String($0)
+ }) else {
+ return nil
+ }
+ self = smol
+ }
+ init?(_ codeUnits: Array<UInt16>) {
+ guard let smol = codeUnits.withUnsafeBufferPointer({
+ return _SmallUTF8String($0)
+ }) else {
+ return nil
+ }
+ self = smol
+ }
+}
+
SmallStringTests.test("FitsInSmall") {
func runTest(_ input: String) throws {
let tiny = Array(input.utf8)
@@ -68,6 +88,15 @@
//
expectThrows("Didn't fit", { try runTest("0123456789abcdef") })
expectThrows("Didn't fit", { try runTest("👩👦👦") })
+
+ for cu in (0 as UInt32)...(0x10FFFF as UInt32) {
+ // TODO: Iterate over all scalars when we support UTF-8, and possibly move
+ // this to validation suite.
+ guard let scalar = Unicode.Scalar(cu) else { continue }
+ guard cu <= 0x7F else { break }
+ expectDoesNotThrow({ try runTest(String(scalar)) })
+ }
+
}
SmallStringTests.test("Bridging") {
diff --git a/test/stdlib/StringIndex.swift b/test/stdlib/StringIndex.swift
new file mode 100644
index 0000000..925ef65
--- /dev/null
+++ b/test/stdlib/StringIndex.swift
@@ -0,0 +1,134 @@
+// RUN: %target-run-simple-swift
+// REQUIRES: executable_test
+
+import StdlibUnittest
+
+var StringIndexTests = TestSuite("StringIndexTests")
+
+enum SimpleString: String {
+ case smallASCII = "abcdefg"
+ case smallUnicode = "abéÏ𓀀"
+ case largeASCII = "012345678901234567890"
+ case largeUnicode = "abéÏ012345678901234567890𓀀"
+ case emoji = "😀😃🤢🤮👩🏿🎤🧛🏻♂️🧛🏻♂️👩👩👦👦"
+}
+
+let simpleStrings: [String] = [
+ SimpleString.smallASCII.rawValue,
+ SimpleString.smallUnicode.rawValue,
+ SimpleString.largeASCII.rawValue,
+ SimpleString.largeUnicode.rawValue,
+ SimpleString.emoji.rawValue,
+]
+
+StringIndexTests.test("basic sanity checks") {
+ for s in simpleStrings {
+ let utf8 = Array(s.utf8)
+ let subUTF8 = Array(s[...].utf8)
+ let utf16 = Array(s.utf16)
+ let subUTF16 = Array(s[...].utf16)
+ let utf32 = Array(s.unicodeScalars.map { $0.value })
+ let subUTF32 = Array(s[...].unicodeScalars.map { $0.value })
+
+ expectEqual(s, String(decoding: utf8, as: UTF8.self))
+ expectEqual(s, String(decoding: subUTF8, as: UTF8.self))
+ expectEqual(s, String(decoding: utf16, as: UTF16.self))
+ expectEqual(s, String(decoding: subUTF16, as: UTF16.self))
+ expectEqual(s, String(decoding: utf32, as: UTF32.self))
+ expectEqual(s, String(decoding: subUTF32, as: UTF32.self))
+ }
+}
+
+StringIndexTests.test("view counts") {
+ func validateViewCount<View: BidirectionalCollection>(
+ _ view: View, for string: String,
+ stackTrace: SourceLocStack = SourceLocStack(),
+ showFrame: Bool = true,
+ file: String = #file, line: UInt = #line
+ ) where View.Element: Equatable, View.Index == String.Index {
+
+ var stackTrace = stackTrace.pushIf(showFrame, file: file, line: line)
+
+ let count = view.count
+ func expect(_ i: Int,
+ file: String = #file, line: UInt = #line
+ ) {
+ expectEqual(count, i, "for String: \(string)",
+ stackTrace: stackTrace.pushIf(showFrame, file: file, line: line),
+ showFrame: false)
+ }
+
+ let reversedView = view.reversed()
+
+ expect(Array(view).count)
+ expect(view.indices.count)
+ expect(view.indices.reversed().count)
+ expect(reversedView.indices.count)
+ expect(view.distance(from: view.startIndex, to: view.endIndex))
+ expect(reversedView.distance(
+ from: reversedView.startIndex, to: reversedView.endIndex))
+
+ // Access the elements from the indices
+ expectEqual(Array(view), view.indices.map { view[$0] })
+ expectEqual(
+ Array(reversedView), reversedView.indices.map { reversedView[$0] })
+
+ let indicesArray = Array<String.Index>(view.indices)
+ for i in 0..<indicesArray.count {
+ var idx = view.startIndex
+ idx = view.index(idx, offsetBy: i)
+ expectEqual(indicesArray[i], idx)
+ }
+ }
+
+ for s in simpleStrings {
+ validateViewCount(s, for: s)
+ validateViewCount(s.utf8, for: s)
+ validateViewCount(s.utf16, for: s)
+ validateViewCount(s.unicodeScalars, for: s)
+
+ validateViewCount(s[...], for: s)
+ validateViewCount(s[...].utf8, for: s)
+ validateViewCount(s[...].utf16, for: s)
+ validateViewCount(s[...].unicodeScalars, for: s)
+ }
+}
+
+StringIndexTests.test("interchange") {
+ // Basic index alignment
+
+ func validateIndices(_ s: String) {
+ let utf8Indices = s.utf8.indices
+ let utf16Indices = s.utf16.indices
+ let unicodeScalarIndices = s.unicodeScalars.indices
+ let characterIndices = s.indices
+
+ for idx in utf8Indices {
+ let char = s.utf8[idx]
+
+ // ASCII or leading code unit in the scalar
+ if char <= 0x7F || char >= 0b1100_0000 {
+ expectEqual(idx, idx.samePosition(in: s.unicodeScalars))
+ expectEqual(idx, idx.samePosition(in: s.utf16))
+
+ // ASCII
+ if char <= 0x7F {
+ expectEqual(UInt16(char), s.utf16[idx])
+ expectEqual(UInt32(char), s.unicodeScalars[idx].value)
+ }
+ } else {
+ // Continuation code unit
+ assert(char & 0b1100_0000 == 0b1000_0000)
+ expectNil(idx.samePosition(in: s))
+ expectNil(idx.samePosition(in: s.utf16))
+ expectNil(idx.samePosition(in: s.unicodeScalars))
+ }
+ }
+ }
+
+ for s in simpleStrings {
+ validateIndices(s)
+ }
+}
+
+runAllTests()
\ No newline at end of file
diff --git a/test/stdlib/subString.swift b/test/stdlib/subString.swift
index 7fd6b15..dd24e3d 100644
--- a/test/stdlib/subString.swift
+++ b/test/stdlib/subString.swift
@@ -195,21 +195,30 @@
}
SubstringTests.test("UTF8View") {
- let s = "abcdefg"
- let t = s.utf8.dropFirst(2)
- let u = t.dropFirst(2)
-
- checkMatch(s.utf8, t, t.startIndex)
- checkMatch(s.utf8, t, t.index(after: t.startIndex))
-
- checkMatch(s.utf8, t, u.startIndex)
- checkMatch(t, u, u.startIndex)
- checkMatch(t, u, u.index(after: u.startIndex))
+ let strs = [
+ "abcdefg", // Small ASCII
+ "abéÏ", // Small Unicode
+ "012345678901234567890", // Large ASCII
+ "abéÏ012345678901234567890", // Large Unicode
+ ]
- expectEqual("", String(t.dropFirst(10))!)
- expectEqual("", String(t.dropLast(10))!)
- expectEqual("", String(u.dropFirst(10))!)
- expectEqual("", String(u.dropLast(10))!)
+ for s in strs {
+ let count = s.count
+ let t = s.utf8.dropFirst(2)
+ let u = t.dropFirst(2)
+
+ checkMatch(s.utf8, t, t.startIndex)
+ checkMatch(s.utf8, t, t.index(after: t.startIndex))
+
+ checkMatch(s.utf8, t, u.startIndex)
+ checkMatch(t, u, u.startIndex)
+ checkMatch(t, u, u.index(after: u.startIndex))
+
+ expectEqual("", String(t.dropFirst(100))!)
+ expectEqual("", String(t.dropLast(100))!)
+ expectEqual("", String(u.dropFirst(100))!)
+ expectEqual("", String(u.dropLast(100))!)
+ }
}
SubstringTests.test("Persistent Content") {
diff --git a/validation-test/stdlib/String.swift b/validation-test/stdlib/String.swift
index 56e908f..fd0a840 100644
--- a/validation-test/stdlib/String.swift
+++ b/validation-test/stdlib/String.swift
@@ -942,7 +942,7 @@
expectEqual(isSwiftNative(base), startedNative)
let originalBuffer = base.bufferID
- let isUnique = base._guts.isUniqueNative()
+ let isUnique = base._guts._isUniqueNative()
let startedUnique =
startedNative &&
base._guts._objectIdentifier != nil &&
@@ -1102,7 +1102,7 @@
) {
var chars = Array(String(initialValue).utf8)
modification(&chars)
- let str = String._fromWellFormedUTF8CodeUnitSequence(chars)
+ let str = String(decoding: chars, as: UTF8.self)
expectNil(Int(str))
}
diff --git a/validation-test/stdlib/StringViews.swift b/validation-test/stdlib/StringViews.swift
index 47b3c2b..55fff3f 100644
--- a/validation-test/stdlib/StringViews.swift
+++ b/validation-test/stdlib/StringViews.swift
@@ -9,7 +9,7 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
-// RUN: %target-run-simple-swift
+// RUN: %target-run-stdlib-swift
// REQUIRES: executable_test
import Swift
@@ -37,7 +37,10 @@
// UTF16, grapheme clusters composed of multiple Unicode scalars, and
// invalid UTF16 that should be replaced with replacement characters.
let winterUTF16 = Array("🏂☃❅❆❄︎⛄️❄️".utf16) + [0xD83C, 0x0020, 0xDF67, 0xD83C]
-var winter = _StringGuts._createStringFromUTF16(winterUTF16)
+var winter = winterUTF16.withUnsafeBufferPointer { bufPtr in
+ _StringGuts._createStringFromUTF16(bufPtr)
+}
+
let winterInvalidUTF8: [UTF8.CodeUnit] = replacementUTF8 + ([0x20] as [UTF8.CodeUnit]) + replacementUTF8 + replacementUTF8
let winterUTF8: [UTF8.CodeUnit] = [
0xf0, 0x9f, 0x8f, 0x82, 0xe2, 0x98, 0x83, 0xe2, 0x9d, 0x85, 0xe2,