Files
swift-mirror/stdlib/public/core/StringGutsVisitor.swift
T
Michael Ilseman 9ba2f4c771 [string] Introduce the StringGuts visitor
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.
2018-03-13 15:32:19 -07:00

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)
}