mirror of
https://github.com/apple/swift.git
synced 2026-06-27 12:25:55 +02:00
9ba2f4c771
Add a visitor-like function which will inspect the bitpattern of _StringGuts and dispatch to the appropriate mechanism. This allows us to keep the core usage pattern in one spot, and tweak the branching scheme as the ABI finalizes. It also reduces the bug surface area by allowing us to maintain resilience in the visitor, instead of by-hand at every use site. It also prevents expression-drift, which the by-hand opaque pattern is susceptible to. Current implementation is very carefully written to avoid excess ARC. Uses need to be very careful about not capturing, or else there will be non-trivial closure contexts and performance will blow up. Both of these aspects will hopefully be fixed soon.
176 lines
5.2 KiB
Swift
176 lines
5.2 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TODO: describe
|
|
|
|
//
|
|
// HACK HACK HACK: For whatever reason, having this directly on String instead
|
|
// of _StringGuts avoids a cascade of ARC. Also note, we can have a global
|
|
// function that forwards, but that function **must not be on _StringGuts**,
|
|
// else ARC.
|
|
//
|
|
extension String {
|
|
@_versioned
|
|
@_inlineable
|
|
@inline(__always)
|
|
func _visit<Result>(
|
|
range: (Range<Int>, performBoundsCheck: Bool)? = nil,
|
|
ascii: /*@convention(thin)*/ (_UnmanagedString<UInt8>) -> Result,
|
|
utf16: /*@convention(thin)*/ (_UnmanagedString<UInt16>) -> Result,
|
|
opaque: /*@convention(thin)*/ (_UnmanagedOpaqueString) -> Result
|
|
) -> Result {
|
|
if _slowPath(_guts._isOpaque) {
|
|
return self._visitOpaque(
|
|
range: range, ascii: ascii, utf16: utf16, opaque: opaque)
|
|
}
|
|
|
|
defer { _fixLifetime(self) }
|
|
if _guts.isASCII {
|
|
var view = _guts._unmanagedASCIIView
|
|
if let (range, boundsCheck) = range {
|
|
if boundsCheck {
|
|
view._boundsCheck(offsetRange: range)
|
|
}
|
|
view = view[range]
|
|
}
|
|
return ascii(view)
|
|
} else {
|
|
var view = _guts._unmanagedUTF16View
|
|
if let (range, boundsCheck) = range {
|
|
if boundsCheck {
|
|
view._boundsCheck(offsetRange: range)
|
|
}
|
|
view = view[range]
|
|
}
|
|
return utf16(view)
|
|
}
|
|
}
|
|
|
|
@_versioned
|
|
@effects(readonly)
|
|
@inline(never)
|
|
func _visitOpaque<Result>(
|
|
range: (Range<Int>, performBoundsCheck: Bool)? = nil,
|
|
ascii: /*@convention(thin)*/ (_UnmanagedString<UInt8>) -> Result,
|
|
utf16: /*@convention(thin)*/ (_UnmanagedString<UInt16>) -> Result,
|
|
opaque: /*@convention(thin)*/ (_UnmanagedOpaqueString) -> Result
|
|
) -> Result {
|
|
_sanityCheck(_guts._isOpaque)
|
|
|
|
// TODO: But can it provide a pointer+length representation?
|
|
defer { _fixLifetime(self) }
|
|
var view = _guts._asOpaque()
|
|
if let (range, boundsCheck) = range {
|
|
if boundsCheck {
|
|
view._boundsCheck(offsetRange: range)
|
|
}
|
|
view = view[range]
|
|
}
|
|
|
|
return opaque(view)
|
|
}
|
|
|
|
@_versioned
|
|
@_inlineable
|
|
@inline(__always)
|
|
func _visit<T, Result>(
|
|
range: (Range<Int>, performBoundsCheck: Bool)?,
|
|
args x: T,
|
|
ascii: /*@convention(thin)*/ (_UnmanagedString<UInt8>, T) -> Result,
|
|
utf16: /*@convention(thin)*/ (_UnmanagedString<UInt16>, T) -> Result,
|
|
opaque: /*@convention(thin)*/ (_UnmanagedOpaqueString, T) -> Result
|
|
) -> Result {
|
|
if _slowPath(_guts._isOpaque) {
|
|
return self._visitOpaque(
|
|
range: range, args: x, ascii: ascii, utf16: utf16, opaque: opaque)
|
|
}
|
|
|
|
defer { _fixLifetime(self) }
|
|
if _guts.isASCII {
|
|
var view = _guts._unmanagedASCIIView
|
|
if let (range, boundsCheck) = range {
|
|
if boundsCheck {
|
|
view._boundsCheck(offsetRange: range)
|
|
}
|
|
view = view[range]
|
|
}
|
|
return ascii(view, x)
|
|
} else {
|
|
var view = _guts._unmanagedUTF16View
|
|
if let (range, boundsCheck) = range {
|
|
if boundsCheck {
|
|
view._boundsCheck(offsetRange: range)
|
|
}
|
|
view = view[range]
|
|
}
|
|
return utf16(view, x)
|
|
}
|
|
}
|
|
|
|
@_versioned
|
|
@effects(readonly)
|
|
@inline(never)
|
|
func _visitOpaque<T, Result>(
|
|
range: (Range<Int>, performBoundsCheck: Bool)?,
|
|
args x: T,
|
|
ascii: /*@convention(thin)*/ (_UnmanagedString<UInt8>, T) -> Result,
|
|
utf16: /*@convention(thin)*/ (_UnmanagedString<UInt16>, T) -> Result,
|
|
opaque: /*@convention(thin)*/ (_UnmanagedOpaqueString, T) -> Result
|
|
) -> Result {
|
|
_sanityCheck(_guts._isOpaque)
|
|
|
|
// TODO: But can it provide a pointer+length representation?
|
|
defer { _fixLifetime(self) }
|
|
var view = _guts._asOpaque()
|
|
if let (range, boundsCheck) = range {
|
|
if boundsCheck {
|
|
view._boundsCheck(offsetRange: range)
|
|
}
|
|
view = view[range]
|
|
}
|
|
|
|
return opaque(view, x)
|
|
}
|
|
}
|
|
|
|
@_versioned
|
|
@_inlineable
|
|
@inline(__always)
|
|
internal
|
|
func _visitGuts<Result>(
|
|
_ guts: _StringGuts,
|
|
range: (Range<Int>, performBoundsCheck: Bool)? = nil,
|
|
ascii: /*@convention(thin)*/ (_UnmanagedString<UInt8>) -> Result,
|
|
utf16: /*@convention(thin)*/ (_UnmanagedString<UInt16>) -> Result,
|
|
opaque: /*@convention(thin)*/ (_UnmanagedOpaqueString) -> Result
|
|
) -> Result {
|
|
return String(guts)._visit(
|
|
range: range, ascii: ascii, utf16: utf16, opaque: opaque)
|
|
}
|
|
|
|
@_versioned
|
|
@_inlineable
|
|
@inline(__always)
|
|
internal
|
|
func _visitGuts<T, Result>(
|
|
_ guts: _StringGuts,
|
|
range: (Range<Int>, performBoundsCheck: Bool)? = nil,
|
|
args x: T,
|
|
ascii: /*@convention(thin)*/ (_UnmanagedString<UInt8>, T) -> Result,
|
|
utf16: /*@convention(thin)*/ (_UnmanagedString<UInt16>, T) -> Result,
|
|
opaque: /*@convention(thin)*/ (_UnmanagedOpaqueString, T) -> Result
|
|
) -> Result {
|
|
return String(guts)._visit(
|
|
range: range, args: x, ascii: ascii, utf16: utf16, opaque: opaque)
|
|
}
|
|
|