blob: 8bb704fedec61094c3bcaca448e47809ebfeccf4 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
extension String {
// FIXME(strings): at least temporarily remove it to see where it was applied
/// Creates a new string from the given substring.
///
/// - Parameter substring: A substring to convert to a standalone `String`
/// instance.
///
/// - Complexity: O(*n*), where *n* is the length of `substring`.
public init(_ substring: Substring) {
self = String(substring._slice)
}
}
/// A slice of a string.
///
/// When you create a slice of a string, a `Substring` instance is the result.
/// Operating on substrings is fast and efficient because a substring shares
/// its storage with the original string. The `Substring` type presents the
/// same interface as `String`, so you can avoid or defer any copying of the
/// string's contents.
///
/// The following example creates a `greeting` string, and then finds the
/// substring of the first sentence:
///
/// let greeting = "Hi there! It's nice to meet you! 👋"
/// let endOfSentence = greeting.index(of: "!")!
/// let firstSentence = greeting[...endOfSentence]
/// // firstSentence == "Hi there!"
///
/// You can perform many string operations on a substring. Here, we find the
/// length of the first sentence and create an uppercase version.
///
/// print("'\(firstSentence)' is \(firstSentence.count) characters long.")
/// // Prints "'Hi there!' is 9 characters long."
///
/// let shoutingSentence = firstSentence.uppercased()
/// // shoutingSentence == "HI THERE!"
///
/// Converting a Substring to a String
/// ==================================
///
/// This example defines a `rawData` string with some unstructured data, and
/// then uses the string's `prefix(while:)` method to create a substring of
/// the numeric prefix:
///
/// let rawInput = "126 a.b 22219 zzzzzz"
/// let numericPrefix = rawInput.prefix(while: { "0"..."9" ~= $0 })
/// // numericPrefix is the substring "126"
///
/// When you need to store a substring or pass it to a function that requires a
/// `String` instance, convert it using the `String.init(_:)` initializer.
///
/// _ = Int(numericPrefix, radix: 10)
/// // error: cannot convert value...
/// let prefix = Int(String(numericPrefix), radix: 10)
/// // prefix == 126
///
/// Calling this initializer copies the contents of the substring to a new
/// string.
///
/// - Important: Don't store substrings longer than you need them to perform a
/// specific operation. A substring holds a reference to the entire storage
/// of the string it comes from, not just to the portion it presents, even
/// when there is no other reference to the original string. Storing
/// substrings may, therefore, prolong the lifetime of string data that is
/// no longer otherwise accessible, which can appear to be memory leakage.
public struct Substring : StringProtocol {
public typealias Index = String.Index
public typealias IndexDistance = String.IndexDistance
public typealias SubSequence = Substring
internal var _slice: RangeReplaceableBidirectionalSlice<String>
/// Creates an empty substring.
public init() {
_slice = RangeReplaceableBidirectionalSlice()
}
internal init(_slice: RangeReplaceableBidirectionalSlice<String>) {
self._slice = _slice
}
/// Creates a substring with the specified bounds within the given string.
///
/// - Parameters:
/// - base: The string to create a substring of.
/// - bounds: The range of `base` to use for the substring. The lower and
/// upper bounds of `bounds` must be valid indices of `base`.
public init(_base base: String, _ bounds: Range<Index>) {
_slice = RangeReplaceableBidirectionalSlice(base: base, bounds: bounds)
}
internal init<R: RangeExpression>(
_base base: String, _ bounds: R
) where R.Bound == Index {
self.init(_base: base, bounds.relative(to: base))
}
public var startIndex: Index { return _slice.startIndex }
public var endIndex: Index { return _slice.endIndex }
public func index(after i: Index) -> Index {
_precondition(i < endIndex, "Cannot increment beyond endIndex")
_precondition(i >= startIndex, "Cannot increment an invalid index")
// FIXME(strings): slice types currently lack necessary bound checks
return _slice.index(after: i)
}
public func index(before i: Index) -> Index {
_precondition(i <= endIndex, "Cannot decrement an invalid index")
_precondition(i > startIndex, "Cannot decrement beyond startIndex")
// FIXME(strings): slice types currently lack necessary bound checks
return _slice.index(before: i)
}
public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
let result = _slice.index(i, offsetBy: n)
// FIXME(strings): slice types currently lack necessary bound checks
_precondition(
(_slice._startIndex ... _slice.endIndex).contains(result),
"Operation results in an invalid index")
return result
}
public func index(
_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index
) -> Index? {
let result = _slice.index(i, offsetBy: n, limitedBy: limit)
// FIXME(strings): slice types currently lack necessary bound checks
_precondition(result.map {
(_slice._startIndex ... _slice.endIndex).contains($0)
} ?? true,
"Operation results in an invalid index")
return result
}
public func distance(from start: Index, to end: Index) -> IndexDistance {
return _slice.distance(from: start, to: end)
}
public subscript(i: Index) -> Character {
return _slice[i]
}
public mutating func replaceSubrange<C>(
_ bounds: Range<Index>,
with newElements: C
) where C : Collection, C.Iterator.Element == Iterator.Element {
// FIXME(strings): slice types currently lack necessary bound checks
_slice.replaceSubrange(bounds, with: newElements)
}
% for Range in ['Range', 'ClosedRange']:
public mutating func replaceSubrange(
_ bounds: ${Range}<Index>, with newElements: Substring
) {
replaceSubrange(bounds, with: newElements._slice)
}
% end
/// Creates a string from the given Unicode code units in the specified
/// encoding.
///
/// - Parameters:
/// - codeUnits: A collection of code units encoded in the ecoding
/// specified in `sourceEncoding`.
/// - sourceEncoding: The encoding in which `codeUnits` should be
/// interpreted.
public init<C: Collection, Encoding: _UnicodeEncoding>(
decoding codeUnits: C, as sourceEncoding: Encoding.Type
) where C.Iterator.Element == Encoding.CodeUnit {
self.init(String(decoding: codeUnits, as: sourceEncoding))
}
/// Creates a string from the null-terminated, UTF-8 encoded sequence of
/// bytes at the given pointer.
///
/// - Parameter nullTerminatedUTF8: A pointer to a sequence of contiguous,
/// UTF-8 encoded bytes ending just before the first zero byte.
public init(cString nullTerminatedUTF8: UnsafePointer<CChar>) {
self.init(String(cString: nullTerminatedUTF8))
}
/// Creates a string from the null-terminated sequence of bytes at the given
/// pointer.
///
/// - Parameters:
/// - nullTerminatedCodeUnits: A pointer to a sequence of contiguous code
/// units in the encoding specified in `sourceEncoding`, ending just
/// before the first zero code unit.
/// - sourceEncoding: The encoding in which the code units should be
/// interpreted.
public init<Encoding: _UnicodeEncoding>(
decodingCString nullTerminatedCodeUnits: UnsafePointer<Encoding.CodeUnit>,
as sourceEncoding: Encoding.Type
) {
self.init(
String(decodingCString: nullTerminatedCodeUnits, as: sourceEncoding))
}
/// Calls the given closure with a pointer to the contents of the string,
/// represented as a null-terminated sequence of UTF-8 code units.
///
/// The pointer passed as an argument to `body` is valid only during the
/// execution of `withCString(_:)`. Do not store or return the pointer for
/// later use.
///
/// - Parameter body: A closure with a pointer parameter that points to a
/// null-terminated sequence of UTF-8 code units. If `body` has a return
/// value, that value is also used as the return value for the
/// `withCString(_:)` method. The pointer argument is valid only for the
/// duration of the method's execution.
/// - Returns: The return value, if any, of the `body` closure parameter.
public func withCString<Result>(
_ body: (UnsafePointer<CChar>) throws -> Result) rethrows -> Result {
return try _slice._base._core._withCSubstringAndLength(
in: startIndex._base._position..<endIndex._base._position,
encoding: UTF8.self) {
p, length in try p.withMemoryRebound(to: CChar.self, capacity: length) {
try body($0)
}
}
}
/// Calls the given closure with a pointer to the contents of the string,
/// represented as a null-terminated sequence of code units.
///
/// The pointer passed as an argument to `body` is valid only during the
/// execution of `withCString(encodedAs:_:)`. Do not store or return the
/// pointer for later use.
///
/// - Parameters:
/// - body: A closure with a pointer parameter that points to a
/// null-terminated sequence of code units. If `body` has a return
/// value, that value is also used as the return value for the
/// `withCString(encodedAs:_:)` method. The pointer argument is valid
/// only for the duration of the method's execution.
/// - targetEncoding: The encoding in which the code units should be
/// interpreted.
/// - Returns: The return value, if any, of the `body` closure parameter.
public func withCString<Result, TargetEncoding: _UnicodeEncoding>(
encodedAs targetEncoding: TargetEncoding.Type,
_ body: (UnsafePointer<TargetEncoding.CodeUnit>) throws -> Result
) rethrows -> Result {
return try _slice._base._core._withCSubstring(
in: startIndex._base._position..<endIndex._base._position,
encoding: targetEncoding, body)
}
}
extension Substring : _SwiftStringView {
var _persistentContent : String {
let wholeCore = _slice._base._core
let native = wholeCore.nativeBuffer
if _fastPath(native != nil), let n = native {
if _fastPath(
n.start == wholeCore._baseAddress && n.usedCount == wholeCore.count) {
return String(wholeCore)
}
}
var r = String()
r._core.append(_ephemeralContent._core)
_sanityCheck(r._core._owner !== _ephemeralContent._core._owner)
return r
}
public // @testablw
var _ephemeralContent : String {
let wholeCore = _slice._base._core
let subCore : _StringCore = wholeCore[
startIndex._base._position..<endIndex._base._position]
// Check that we haven't allocated a new buffer for the result, if we have
// contiguous storage.
_sanityCheck(
subCore._owner === wholeCore._owner || !wholeCore.hasContiguousStorage)
return String(subCore)
}
}
extension Substring : CustomReflectable {
public var customMirror: Mirror {
return String(self).customMirror
}
}
extension Substring : CustomPlaygroundQuickLookable {
public var customPlaygroundQuickLook: PlaygroundQuickLook {
return String(self).customPlaygroundQuickLook
}
}
extension Substring : CustomStringConvertible {
public var description: String {
return String(self)
}
}
extension Substring : CustomDebugStringConvertible {
public var debugDescription: String {
return String(self).debugDescription
}
}
extension Substring : LosslessStringConvertible {
public init(_ content: String) {
self.init(_base: content, content.startIndex ..< content.endIndex)
}
}
extension StringProtocol {
public static func ==<R: StringProtocol>(lhs: Self, rhs: R) -> Bool {
return lhs._ephemeralString == rhs._ephemeralString
}
public static func !=<R: StringProtocol>(lhs: Self, rhs: R) -> Bool {
return lhs._ephemeralString != rhs._ephemeralString
}
}
extension StringProtocol {
public static func < <R: StringProtocol>(lhs: Self, rhs: R) -> Bool {
return lhs._ephemeralString < rhs._ephemeralString
}
public static func > <R: StringProtocol>(lhs: Self, rhs: R) -> Bool {
return rhs < lhs
}
public static func <= <R: StringProtocol>(lhs: Self, rhs: R) -> Bool {
return !(rhs < lhs)
}
public static func >= <R: StringProtocol>(lhs: Self, rhs: R) -> Bool {
return !(lhs < rhs)
}
}
extension StringProtocol {
public var hashValue : Int {
return self._ephemeralString.hashValue
}
}
extension Substring {
% for (property, ViewPrefix) in [
% ('utf8', 'UTF8'),
% ('utf16', 'UTF16'),
% ('unicodeScalars', 'UnicodeScalar'),
% ('characters', 'Character')]:
public typealias ${ViewPrefix}Index = String.${ViewPrefix}View.Index
public var ${property}: String.${ViewPrefix}View {
get {
return String(self).${property}
}
set {
let base = String(describing: newValue)
self = Substring(_base: base, base.startIndex ..< base.endIndex)
}
}
% end
}
#if _runtime(_ObjC)
extension Substring {
public func hasPrefix(_ prefix: String) -> Bool {
return String(self).hasPrefix(prefix)
}
public func hasSuffix(_ suffix: String) -> Bool {
return String(self).hasSuffix(suffix)
}
}
#endif
extension Substring : RangeReplaceableCollection {
public init<S : Sequence>(_ elements: S)
where S.Element == Character {
let e0 = elements as? _SwiftStringView
if _fastPath(e0 != nil), let e = e0 {
self = e._ephemeralContent[...]
}
else {
self = String(elements)[...]
}
}
public mutating func append<S : Sequence>(contentsOf elements: S)
where S.Element == Character {
var c = self._ephemeralContent._core
self = Substring()
let e0 = elements as? _SwiftStringView
if _fastPath(e0 != nil), let e1 = e0 {
c.append(contentsOf: e1._ephemeralContent.utf16)
self = String(c)[...]
}
else {
var s = String(c)
s.append(contentsOf: elements)
self = s[...]
}
}
}
extension Substring {
public func lowercased() -> String {
return String(self).lowercased()
}
public func uppercased() -> String {
return String(self).uppercased()
}
}
extension Substring : TextOutputStream {
public mutating func write(_ other: String) {
append(contentsOf: other)
}
}
extension Substring : TextOutputStreamable {
public func write<Target : TextOutputStream>(to target: inout Target) {
target.write(String(self))
}
}
extension Substring : ExpressibleByUnicodeScalarLiteral {
public init(unicodeScalarLiteral value: String) {
self.init(_base: value, value.startIndex ..< value.endIndex)
}
}
extension Substring : ExpressibleByExtendedGraphemeClusterLiteral {
public init(extendedGraphemeClusterLiteral value: String) {
self.init(_base: value, value.startIndex ..< value.endIndex)
}
}
extension Substring : ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self.init(_base: value, value.startIndex ..< value.endIndex)
}
}
//===--- String/Substring Slicing Support ---------------------------------===//
/// In Swift 3.2, in the absence of type context,
///
/// someString[someString.startIndex..<someString.endIndex]
///
/// was deduced to be of type `String`. Therefore have a more-specific
/// Swift-3-only `subscript` overload on `String` (and `Substring`) that
/// continues to produce `String`.
extension String {
@available(swift, introduced: 4)
public subscript(r: Range<Index>) -> Substring {
return Substring(
_slice: RangeReplaceableBidirectionalSlice(base: self, bounds: r))
}
@available(swift, obsoleted: 4)
public subscript(bounds: Range<Index>) -> String {
return String(characters[bounds])
}
@available(swift, obsoleted: 4)
public subscript(bounds: ClosedRange<Index>) -> String {
return String(characters[bounds])
}
}
extension Substring {
@available(swift, introduced: 4)
public subscript(r: Range<Index>) -> Substring {
return Substring(_slice: _slice[r])
}
@available(swift, obsoleted: 4)
public subscript(bounds: Range<Index>) -> String {
return String(characters[bounds])
}
@available(swift, obsoleted: 4)
public subscript(bounds: ClosedRange<Index>) -> String {
return String(characters[bounds])
}
}
//===----------------------------------------------------------------------===//