blob: 25b8cee8e4d6d432affdf3d01f986ad7f29cf326 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// String is a bidirectional collection of `Character`s, aka graphemes
extension String: BidirectionalCollection {
/// A type that represents the number of steps between two `String.Index`
/// values, where one value is reachable from the other.
///
/// In Swift, *reachability* refers to the ability to produce one value from
/// the other through zero or more applications of `index(after:)`.
public typealias IndexDistance = Int
public typealias SubSequence = Substring
/// The position of the first character in a nonempty string.
///
/// In an empty string, `startIndex` is equal to `endIndex`.
@inlinable // FIXME(sil-serialize-all)
public var startIndex: Index { return Index(encodedOffset: 0) }
/// A string's "past the end" position---that is, the position one greater
/// than the last valid subscript argument.
///
/// In an empty string, `endIndex` is equal to `startIndex`.
@inlinable // FIXME(sil-serialize-all)
public var endIndex: Index { return Index(encodedOffset: _guts.count) }
/// The number of characters in a string.
public var count: Int {
return distance(from: startIndex, to: endIndex)
}
/// Returns the position immediately after the given index.
///
/// - Parameter i: A valid index of the collection. `i` must be less than
/// `endIndex`.
/// - Returns: The index value immediately after `i`.
public func index(after i: Index) -> Index {
return _visitGuts(_guts, args: i,
ascii: { ascii, i in ascii.characterIndex(after: i) },
utf16: { utf16, i in utf16.characterIndex(after: i) },
opaque: { opaque, i in opaque.characterIndex(after: i) })
}
/// Returns the position immediately before the given index.
///
/// - Parameter i: A valid index of the collection. `i` must be greater than
/// `startIndex`.
/// - Returns: The index value immediately before `i`.
public func index(before i: Index) -> Index {
return _visitGuts(_guts, args: i,
ascii: { ascii, i in ascii.characterIndex(before: i) },
utf16: { utf16, i in utf16.characterIndex(before: i) },
opaque: { opaque, i in opaque.characterIndex(before: i) })
}
/// Returns an index that is the specified distance from the given index.
///
/// The following example obtains an index advanced four positions from a
/// string's starting index and then prints the character at that position.
///
/// let s = "Swift"
/// let i = s.index(s.startIndex, offsetBy: 4)
/// print(s[i])
/// // Prints "t"
///
/// The value passed as `distance` must not offset `i` beyond the bounds of
/// the collection.
///
/// - Parameters:
/// - i: A valid index of the collection.
/// - distance: The distance to offset `i`.
/// - Returns: An index offset by `distance` from the index `i`. If
/// `distance` is positive, this is the same value as the result of
/// `distance` calls to `index(after:)`. If `distance` is negative, this
/// is the same value as the result of `abs(distance)` calls to
/// `index(before:)`.
///
/// - Complexity: O(*k*), where *k* is the absolute value of `distance`.
public func index(_ i: Index, offsetBy distance: IndexDistance) -> Index {
return _visitGuts(_guts, args: (i, distance),
ascii: { ascii, args in let (i, n) = args
return ascii.characterIndex(i, offsetBy: n) },
utf16: { utf16, args in let (i, n) = args
return utf16.characterIndex(i, offsetBy: n) },
opaque: { opaque, args in let (i, n) = args
return opaque.characterIndex(i, offsetBy: n) })
}
/// Returns an index that is the specified distance from the given index,
/// unless that distance is beyond a given limiting index.
///
/// The following example obtains an index advanced four positions from a
/// string's starting index and then prints the character at that position.
/// The operation doesn't require going beyond the limiting `s.endIndex`
/// value, so it succeeds.
///
/// let s = "Swift"
/// if let i = s.index(s.startIndex, offsetBy: 4, limitedBy: s.endIndex) {
/// print(s[i])
/// }
/// // Prints "t"
///
/// The next example attempts to retrieve an index six positions from
/// `s.startIndex` but fails, because that distance is beyond the index
/// passed as `limit`.
///
/// let j = s.index(s.startIndex, offsetBy: 6, limitedBy: s.endIndex)
/// print(j)
/// // Prints "nil"
///
/// The value passed as `distance` must not offset `i` beyond the bounds of the
/// collection, unless the index passed as `limit` prevents offsetting
/// beyond those bounds.
///
/// - Parameters:
/// - i: A valid index of the collection.
/// - distance: The distance to offset `i`.
/// - limit: A valid index of the collection to use as a limit. If `distance > 0`,
/// a limit that is less than `i` has no effect. Likewise, if `distance < 0`, a
/// limit that is greater than `i` has no effect.
/// - Returns: An index offset by `distance` from the index `i`, unless that index
/// would be beyond `limit` in the direction of movement. In that case,
/// the method returns `nil`.
///
/// - Complexity: O(*k*), where *k* is the absolute value of `distance`.
public func index(
_ i: Index, offsetBy distance: IndexDistance, limitedBy limit: Index
) -> Index? {
return _visitGuts(_guts, args: (i, distance, limit),
ascii: { ascii, args in let (i, n, limit) = args
return ascii.characterIndex(i, offsetBy: n, limitedBy: limit) },
utf16: { utf16, args in let (i, n, limit) = args
return utf16.characterIndex(i, offsetBy: n, limitedBy: limit) },
opaque: { opaque, args in let (i, n, limit) = args
return opaque.characterIndex(i, offsetBy: n, limitedBy: limit) })
}
/// Returns the distance between two indices.
///
/// - Parameters:
/// - start: A valid index of the collection.
/// - end: Another valid index of the collection. If `end` is equal to
/// `start`, the result is zero.
/// - Returns: The distance between `start` and `end`.
///
/// - Complexity: O(*k*), where *k* is the resulting distance.
public func distance(from start: Index, to end: Index) -> IndexDistance {
return _visitGuts(_guts, args: (start, end),
ascii: { ascii, args in let (start, end) = args
return ascii.characterDistance(from: start, to: end) },
utf16: { utf16, args in let (start, end) = args
return utf16.characterDistance(from: start, to: end) },
opaque: { opaque, args in let (start, end) = args
return opaque.characterDistance(from: start, to: end) })
}
/// Accesses the character at the given position.
///
/// You can use the same indices for subscripting a string and its substring.
/// For example, this code finds the first letter after the first space:
///
/// let str = "Greetings, friend! How are you?"
/// let firstSpace = str.firstIndex(of: " ") ?? str.endIndex
/// let substr = str[firstSpace...]
/// if let nextCapital = substr.firstIndex(where: { $0 >= "A" && $0 <= "Z" }) {
/// print("Capital after a space: \(str[nextCapital])")
/// }
/// // Prints "Capital after a space: H"
///
/// - Parameter i: A valid index of the string. `i` must be less than the
/// string's end index.
public subscript(i: Index) -> Character {
return _visitGuts(_guts, args: i,
ascii: { ascii, i in return ascii.character(at: i) },
utf16: { utf16, i in return utf16.character(at: i) },
opaque: { opaque, i in return opaque.character(at: i) })
}
}