mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
319 lines
9.8 KiB
Swift
319 lines
9.8 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// COW helpers
|
|
extension _StringGuts {
|
|
internal var nativeCapacity: Int? {
|
|
guard hasNativeStorage else { return nil }
|
|
return _object.nativeStorage.capacity
|
|
}
|
|
|
|
internal var nativeUnusedCapacity: Int? {
|
|
guard hasNativeStorage else { return nil }
|
|
return _object.nativeStorage.unusedCapacity
|
|
}
|
|
|
|
// If natively stored and uniquely referenced, return the storage's total
|
|
// capacity. Otherwise, nil.
|
|
internal var uniqueNativeCapacity: Int? {
|
|
@inline(__always) mutating get {
|
|
guard isUniqueNative else { return nil }
|
|
return _object.nativeStorage.capacity
|
|
}
|
|
}
|
|
|
|
// If natively stored and uniquely referenced, return the storage's spare
|
|
// capacity. Otherwise, nil.
|
|
internal var uniqueNativeUnusedCapacity: Int? {
|
|
@inline(__always) mutating get {
|
|
guard isUniqueNative else { return nil }
|
|
return _object.nativeStorage.unusedCapacity
|
|
}
|
|
}
|
|
|
|
@usableFromInline // @testable
|
|
internal var isUniqueNative: Bool {
|
|
@inline(__always) mutating get {
|
|
// Note: mutating so that self is `inout`.
|
|
guard hasNativeStorage else { return false }
|
|
defer { _fixLifetime(self) }
|
|
var bits: UInt = _object.largeAddressBits
|
|
return _isUnique_native(&bits)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Range-replaceable operation support
|
|
extension _StringGuts {
|
|
@inlinable
|
|
internal init(_initialCapacity capacity: Int) {
|
|
self.init()
|
|
if _slowPath(capacity > _SmallString.capacity) {
|
|
self.grow(capacity)
|
|
}
|
|
}
|
|
|
|
internal mutating func reserveCapacity(_ n: Int) {
|
|
// Check if there's nothing to do
|
|
if n <= _SmallString.capacity { return }
|
|
if let currentCap = self.uniqueNativeCapacity, currentCap >= n { return }
|
|
|
|
// Grow
|
|
self.grow(n)
|
|
}
|
|
|
|
// Grow to accomodate at least `n` code units
|
|
@usableFromInline
|
|
internal mutating func grow(_ n: Int) {
|
|
defer { self._invariantCheck() }
|
|
|
|
_internalInvariant(
|
|
self.uniqueNativeCapacity == nil || self.uniqueNativeCapacity! < n)
|
|
|
|
let growthTarget = Swift.max(n, (self.uniqueNativeCapacity ?? 0) * 2)
|
|
|
|
if _fastPath(isFastUTF8) {
|
|
let isASCII = self.isASCII
|
|
let storage = self.withFastUTF8 {
|
|
__StringStorage.create(
|
|
initializingFrom: $0, capacity: growthTarget, isASCII: isASCII)
|
|
}
|
|
|
|
self = _StringGuts(storage)
|
|
return
|
|
}
|
|
|
|
_foreignGrow(growthTarget)
|
|
}
|
|
|
|
@inline(never) // slow-path
|
|
private mutating func _foreignGrow(_ n: Int) {
|
|
// TODO(String performance): Skip intermediary array, transcode directly
|
|
// into a StringStorage space.
|
|
let selfUTF8 = Array(String(self).utf8)
|
|
selfUTF8.withUnsafeBufferPointer {
|
|
self = _StringGuts(__StringStorage.create(
|
|
initializingFrom: $0, capacity: n, isASCII: self.isASCII))
|
|
}
|
|
}
|
|
|
|
// Ensure unique native storage with sufficient capacity for the following
|
|
// append.
|
|
private mutating func prepareForAppendInPlace(
|
|
otherUTF8Count otherCount: Int
|
|
) {
|
|
defer {
|
|
_internalInvariant(self.uniqueNativeUnusedCapacity != nil,
|
|
"growth should produce uniqueness")
|
|
_internalInvariant(self.uniqueNativeUnusedCapacity! >= otherCount,
|
|
"growth should produce enough capacity")
|
|
}
|
|
|
|
// See if we can accomodate without growing or copying. If we have
|
|
// sufficient capacity, we do not need to grow, and we can skip the copy if
|
|
// unique. Otherwise, growth is required.
|
|
let sufficientCapacity: Bool
|
|
if let unused = self.nativeUnusedCapacity, unused >= otherCount {
|
|
sufficientCapacity = true
|
|
} else {
|
|
sufficientCapacity = false
|
|
}
|
|
if self.isUniqueNative && sufficientCapacity {
|
|
return
|
|
}
|
|
|
|
let totalCount = self.utf8Count + otherCount
|
|
|
|
// Non-unique storage: just make a copy of the appropriate size, otherwise
|
|
// grow like an array.
|
|
let growthTarget: Int
|
|
if sufficientCapacity {
|
|
growthTarget = totalCount
|
|
} else {
|
|
growthTarget = Swift.max(
|
|
totalCount, _growArrayCapacity(nativeCapacity ?? 0))
|
|
}
|
|
self.grow(growthTarget)
|
|
}
|
|
|
|
internal mutating func append(_ other: _StringGuts) {
|
|
if self.isSmall && other.isSmall {
|
|
if let smol = _SmallString(self.asSmall, appending: other.asSmall) {
|
|
self = _StringGuts(smol)
|
|
return
|
|
}
|
|
}
|
|
|
|
append(_StringGutsSlice(other))
|
|
}
|
|
|
|
internal mutating func append(_ slicedOther: _StringGutsSlice) {
|
|
defer { self._invariantCheck() }
|
|
|
|
if self.isSmall && slicedOther._guts.isSmall {
|
|
// TODO: In-register slicing
|
|
let smolSelf = self.asSmall
|
|
if let smol = slicedOther.withFastUTF8({ otherUTF8 in
|
|
return _SmallString(smolSelf, appending: _SmallString(otherUTF8)!)
|
|
}) {
|
|
self = _StringGuts(smol)
|
|
return
|
|
}
|
|
}
|
|
|
|
prepareForAppendInPlace(otherUTF8Count: slicedOther.utf8Count)
|
|
|
|
if slicedOther.isFastUTF8 {
|
|
let otherIsASCII = slicedOther.isASCII
|
|
slicedOther.withFastUTF8 { otherUTF8 in
|
|
self.appendInPlace(otherUTF8, isASCII: otherIsASCII)
|
|
}
|
|
return
|
|
}
|
|
|
|
_foreignAppendInPlace(slicedOther)
|
|
}
|
|
|
|
internal mutating func appendInPlace(
|
|
_ other: UnsafeBufferPointer<UInt8>, isASCII: Bool
|
|
) {
|
|
self._object.nativeStorage.appendInPlace(other, isASCII: isASCII)
|
|
|
|
// We re-initialize from the modified storage to pick up new count, flags,
|
|
// etc.
|
|
self = _StringGuts(self._object.nativeStorage)
|
|
}
|
|
|
|
@inline(never) // slow-path
|
|
private mutating func _foreignAppendInPlace(_ other: _StringGutsSlice) {
|
|
_internalInvariant(!other.isFastUTF8)
|
|
_internalInvariant(self.uniqueNativeUnusedCapacity != nil)
|
|
|
|
var iter = Substring(other).utf8.makeIterator()
|
|
self._object.nativeStorage.appendInPlace(&iter, isASCII: other.isASCII)
|
|
|
|
// We re-initialize from the modified storage to pick up new count, flags,
|
|
// etc.
|
|
self = _StringGuts(self._object.nativeStorage)
|
|
}
|
|
|
|
internal mutating func clear() {
|
|
guard isUniqueNative else {
|
|
self = _StringGuts()
|
|
return
|
|
}
|
|
|
|
// Reset the count
|
|
_object.nativeStorage.clear()
|
|
self = _StringGuts(_object.nativeStorage)
|
|
}
|
|
|
|
internal mutating func remove(from lower: Index, to upper: Index) {
|
|
let lowerOffset = lower._encodedOffset
|
|
let upperOffset = upper._encodedOffset
|
|
_internalInvariant(lower.transcodedOffset == 0 && upper.transcodedOffset == 0)
|
|
_internalInvariant(lowerOffset <= upperOffset && upperOffset <= self.count)
|
|
|
|
if isUniqueNative {
|
|
_object.nativeStorage.remove(from: lowerOffset, to: upperOffset)
|
|
// We re-initialize from the modified storage to pick up new count, flags,
|
|
// etc.
|
|
self = _StringGuts(self._object.nativeStorage)
|
|
return
|
|
}
|
|
|
|
// TODO(cleanup): Add append on guts taking range, use that
|
|
var result = String()
|
|
// FIXME: It should be okay to get rid of excess capacity
|
|
// here. rdar://problem/45635432
|
|
result.reserveCapacity(
|
|
nativeCapacity ?? (count &- (upperOffset &- lowerOffset)))
|
|
result.append(contentsOf: String(self)[..<lower])
|
|
result.append(contentsOf: String(self)[upper...])
|
|
self = result._guts
|
|
}
|
|
|
|
@inline(__always) // Always-specialize
|
|
internal mutating func replaceSubrange<C>(
|
|
_ bounds: Range<Index>,
|
|
with newElements: C
|
|
) where C: Collection, C.Iterator.Element == Character {
|
|
if isUniqueNative {
|
|
if let replStr = newElements as? String, replStr._guts.isFastUTF8 {
|
|
replStr._guts.withFastUTF8 {
|
|
uniqueNativeReplaceSubrange(
|
|
bounds, with: $0, isASCII: replStr._guts.isASCII)
|
|
}
|
|
return
|
|
}
|
|
uniqueNativeReplaceSubrange(
|
|
bounds, with: newElements.lazy.flatMap { $0.utf8 })
|
|
return
|
|
}
|
|
|
|
var result = String()
|
|
// FIXME: It should be okay to get rid of excess capacity
|
|
// here. rdar://problem/45635432
|
|
if let capacity = self.nativeCapacity {
|
|
result.reserveCapacity(capacity)
|
|
}
|
|
let selfStr = String(self)
|
|
result.append(contentsOf: selfStr[..<bounds.lowerBound])
|
|
result.append(contentsOf: newElements)
|
|
result.append(contentsOf: selfStr[bounds.upperBound...])
|
|
self = result._guts
|
|
}
|
|
|
|
internal mutating func uniqueNativeReplaceSubrange(
|
|
_ bounds: Range<Index>,
|
|
with codeUnits: UnsafeBufferPointer<UInt8>,
|
|
isASCII: Bool
|
|
) {
|
|
let neededCapacity =
|
|
bounds.lowerBound._encodedOffset
|
|
+ codeUnits.count + (self.count - bounds.upperBound._encodedOffset)
|
|
reserveCapacity(neededCapacity)
|
|
|
|
_internalInvariant(bounds.lowerBound.transcodedOffset == 0)
|
|
_internalInvariant(bounds.upperBound.transcodedOffset == 0)
|
|
|
|
_object.nativeStorage.replace(
|
|
from: bounds.lowerBound._encodedOffset,
|
|
to: bounds.upperBound._encodedOffset,
|
|
with: codeUnits)
|
|
self = _StringGuts(_object.nativeStorage)
|
|
}
|
|
|
|
internal mutating func uniqueNativeReplaceSubrange<C: Collection>(
|
|
_ bounds: Range<Index>,
|
|
with codeUnits: C
|
|
) where C.Element == UInt8 {
|
|
let replCount = codeUnits.count
|
|
|
|
let neededCapacity =
|
|
bounds.lowerBound._encodedOffset
|
|
+ replCount + (self.count - bounds.upperBound._encodedOffset)
|
|
reserveCapacity(neededCapacity)
|
|
|
|
_internalInvariant(bounds.lowerBound.transcodedOffset == 0)
|
|
_internalInvariant(bounds.upperBound.transcodedOffset == 0)
|
|
|
|
_object.nativeStorage.replace(
|
|
from: bounds.lowerBound._encodedOffset,
|
|
to: bounds.upperBound._encodedOffset,
|
|
with: codeUnits,
|
|
replacementCount: replCount)
|
|
self = _StringGuts(_object.nativeStorage)
|
|
}
|
|
}
|
|
|