Files
swift-mirror/stdlib/public/core/StringStorage.swift
Michael Ilseman 1229af8e0f [string] Adopt _StringGuts visitor pattern.
Use the visitor pattern in most of the opaque-by-hand call
sites. Inspecting the compiler output does not show excessive and
unanticipated ARC, but there may need to be further tweaks.

One downside of the visitor pattern as written is that there's extra
shuffling around of registers for the closure CC. Hopefully this will
also be fixed soon.
2018-03-13 15:32:19 -07:00

282 lines
7.7 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
import SwiftShims
@_fixed_layout
public
class _SwiftRawStringStorage : _SwiftNativeNSString {
@nonobjc
public // @testable
final var capacity: Int
@nonobjc
public // @testable
final var count: Int
@nonobjc
internal init(_doNotCallMe: ()) {
_sanityCheckFailure("Use the create method")
}
@_inlineable
@_versioned
@nonobjc
internal var rawStart: UnsafeMutableRawPointer {
_abstract()
}
@_inlineable
@nonobjc
public // @testable
final var unusedCapacity: Int {
_sanityCheck(capacity >= count)
return capacity - count
}
}
internal typealias _ASCIIStringStorage = _SwiftStringStorage<UInt8>
internal typealias _UTF16StringStorage = _SwiftStringStorage<UTF16.CodeUnit>
@_fixed_layout
public final class _SwiftStringStorage<CodeUnit>
: _SwiftRawStringStorage, _NSStringCore
where CodeUnit : UnsignedInteger & FixedWidthInteger {
/// Create uninitialized storage of at least the specified capacity.
@_inlineable
@_versioned
@nonobjc
internal static func create(
capacity: Int,
count: Int = 0
) -> _SwiftStringStorage<CodeUnit> {
_sanityCheck(count >= 0 && count <= capacity)
let storage = Builtin.allocWithTailElems_1(
_SwiftStringStorage<CodeUnit>.self,
capacity._builtinWordValue, CodeUnit.self)
let storageAddr = UnsafeMutableRawPointer(
Builtin.bridgeToRawPointer(storage))
let endAddr = (
storageAddr + _stdlib_malloc_size(storageAddr)
).assumingMemoryBound(to: CodeUnit.self)
storage.capacity = endAddr - storage.start
storage.count = count
_sanityCheck(storage.capacity >= capacity)
return storage
}
@_inlineable
@_versioned
@nonobjc
internal override final var rawStart: UnsafeMutableRawPointer {
return UnsafeMutableRawPointer(start)
}
#if _runtime(_ObjC)
// NSString API
@objc(initWithCoder:)
public convenience init(coder aDecoder: AnyObject) {
_sanityCheckFailure("init(coder:) not implemented for _SwiftStringStorage")
}
@objc(length)
public var length: Int {
return count
}
@objc(characterAtIndex:)
public 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(
_ buffer: UnsafeMutablePointer<UInt16>,
range aRange: _SwiftNSRange
) {
_precondition(aRange.location >= 0 && aRange.length >= 0,
"Range out of bounds")
_precondition(aRange.location + aRange.length <= Int(count),
"Range out of bounds")
let slice = unmanagedView[
aRange.location ..< aRange.location + aRange.length]
slice._copy(
into: UnsafeMutableBufferPointer<UTF16.CodeUnit>(
start: buffer,
count: aRange.length))
_fixLifetime(self)
}
@objc(_fastCharacterContents)
public 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 {
// 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
// reference will make the instance non-unique.
return self
}
#endif // _runtime(_ObjC)
}
extension _SwiftStringStorage {
// Basic properties
@_inlineable
@_versioned
@nonobjc
internal final var start: UnsafeMutablePointer<CodeUnit> {
return UnsafeMutablePointer(Builtin.projectTailElems(self, CodeUnit.self))
}
@_inlineable
@_versioned
@nonobjc
internal final var end: UnsafeMutablePointer<CodeUnit> {
return start + count
}
@_inlineable
@_versioned
@nonobjc
internal final var capacityEnd: UnsafeMutablePointer<CodeUnit> {
return start + capacity
}
@_inlineable
@_versioned
@nonobjc
var usedBuffer: UnsafeMutableBufferPointer<CodeUnit> {
return UnsafeMutableBufferPointer(start: start, count: count)
}
@_inlineable
@_versioned
@nonobjc
var unusedBuffer: UnsafeMutableBufferPointer<CodeUnit> {
return UnsafeMutableBufferPointer(start: end, count: capacity - count)
}
@_inlineable
@_versioned
@nonobjc
var unmanagedView: _UnmanagedString<CodeUnit> {
return _UnmanagedString(start: self.start, count: self.count)
}
}
extension _SwiftStringStorage {
// Append operations
@_inlineable
@_versioned
@nonobjc
internal final func _appendInPlace<OtherCodeUnit>(
_ other: _UnmanagedString<OtherCodeUnit>
)
where OtherCodeUnit : FixedWidthInteger & UnsignedInteger {
let otherCount = Int(other.count)
_sanityCheck(self.count + otherCount <= self.capacity)
other._copy(into: self.unusedBuffer)
self.count += otherCount
}
@_inlineable
@_versioned
@nonobjc
internal final func _appendInPlace(_ other: _UnmanagedOpaqueString) {
let otherCount = Int(other.count)
_sanityCheck(self.count + otherCount <= self.capacity)
other._copy(into: self.unusedBuffer)
self.count += otherCount
}
@_inlineable
@_versioned
@nonobjc
internal final func _appendInPlace<C: Collection>(contentsOf other: C)
where C.Element == CodeUnit {
let otherCount = Int(other.count)
_sanityCheck(self.count + otherCount <= self.capacity)
var (remainder, writtenUpTo) =
other._copyContents(initializing: self.unusedBuffer)
_precondition(remainder.next() == nil, "Collection underreported its count")
_precondition(writtenUpTo == otherCount, "Collection misreported its count")
count += otherCount
}
@_inlineable
@_versioned
@_specialize(where C == Character._SmallUTF16, CodeUnit == UInt8)
@nonobjc
internal final func _appendInPlaceUTF16<C: Collection>(contentsOf other: C)
where C.Element == UInt16 {
let otherCount = Int(other.count)
_sanityCheck(self.count + otherCount <= self.capacity)
// TODO: Use _copyContents(initializing:) for UTF16->UTF16 case
var it = other.makeIterator()
for p in end ..< end + otherCount {
p.pointee = CodeUnit(it.next()!)
}
_precondition(it.next() == nil, "Collection underreported its count")
count += otherCount
}
}
extension _SwiftStringStorage {
@_inlineable
@_versioned
@nonobjc
internal final func _appendInPlace(_ other: _StringGuts, range: Range<Int>) {
_visitGuts(other, range: (range, performBoundsCheck: false),
ascii: { ascii in _appendInPlace(ascii) },
utf16: { utf16 in _appendInPlace(utf16) },
opaque: { opaque in _appendInPlace(opaque) })
}
@_inlineable
@_versioned
@nonobjc
internal final func _appendInPlace(_ other: _StringGuts) {
_visitGuts(other,
ascii: { ascii in _appendInPlace(ascii) },
utf16: { utf16 in _appendInPlace(utf16) },
opaque: { opaque in _appendInPlace(opaque) })
}
@_inlineable
@_versioned
@nonobjc
internal final func _appendInPlace(_ other: String) {
self._appendInPlace(other._guts)
}
@_inlineable
@_versioned
@nonobjc
internal final func _appendInPlace<S : StringProtocol>(_ other: S) {
self._appendInPlace(
other._wholeString._guts,
range: other._encodedOffsetRange)
}
}