blob: 5bd2f041e8e6856495d860809e4a42771d3ac2ee [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftShims
extension String {
/// Creates a string representing the given character repeated the specified
/// number of times.
///
/// For example, use this initializer to create a string with ten `"0"`
/// characters in a row.
///
/// let zeroes = String("0" as Character, count: 10)
/// print(zeroes)
/// // Prints "0000000000"
@available(*, unavailable, message: "Replaced by init(repeating: String, count: Int)")
public init(repeating repeatedValue: Character, count: Int) {
Builtin.unreachable()
}
/// Creates a string representing the given Unicode scalar repeated the
/// specified number of times.
///
/// For example, use this initializer to create a string with ten `"0"`
/// scalars in a row.
///
/// let zeroes = String("0" as UnicodeScalar, count: 10)
/// print(zeroes)
/// // Prints "0000000000"
@available(*, unavailable, message: "Replaced by init(repeating: String, count: Int)")
public init(repeating repeatedValue: UnicodeScalar, count: Int) {
Builtin.unreachable()
}
/// Creates a new string representing the given string repeated the specified
/// number of times.
///
/// For example, use this initializer to create a string with ten `"00"`
/// strings in a row.
///
/// let zeroes = String(repeating: "00", count: 10)
/// print(zeroes)
/// // Prints "00000000000000000000"
///
/// - Parameters:
/// - repeatedValue: The string to repeat.
/// - count: The number of times to repeat `repeatedValue` in the resulting
/// string.
public init(repeating repeatedValue: String, count: Int) {
if count == 0 {
self = ""
return
}
precondition(count > 0, "Negative count not allowed")
let s = repeatedValue
self = String(_storage: _StringBuffer(
capacity: s._core.count * count,
initialSize: 0,
elementWidth: s._core.elementWidth))
for _ in 0..<count {
self += s
}
}
public var _lines : [String] {
return _split(separator: "\n")
}
public func _split(separator: UnicodeScalar) -> [String] {
let scalarSlices = unicodeScalars.split { $0 == separator }
return scalarSlices.map { String($0) }
}
/// A Boolean value indicating whether a string has no characters.
public var isEmpty: Bool {
return _core.count == 0
}
}
extension String {
public init(_ _c: UnicodeScalar) {
self = String._fromWellFormedCodeUnitSequence(
UTF32.self,
input: repeatElement(_c.value, count: 1))
}
}
#if _runtime(_ObjC)
/// Determines if `theString` starts with `prefix` comparing the strings under
/// canonical equivalence.
@_silgen_name("swift_stdlib_NSStringHasPrefixNFD")
func _stdlib_NSStringHasPrefixNFD(_ theString: AnyObject, _ prefix: AnyObject) -> Bool
@_silgen_name("swift_stdlib_NSStringHasPrefixNFDPointer")
func _stdlib_NSStringHasPrefixNFDPointer(_ theString: OpaquePointer, _ prefix: OpaquePointer) -> Bool
/// Determines if `theString` ends with `suffix` comparing the strings under
/// canonical equivalence.
@_silgen_name("swift_stdlib_NSStringHasSuffixNFD")
func _stdlib_NSStringHasSuffixNFD(_ theString: AnyObject, _ suffix: AnyObject) -> Bool
@_silgen_name("swift_stdlib_NSStringHasSuffixNFDPointer")
func _stdlib_NSStringHasSuffixNFDPointer(_ theString: OpaquePointer, _ suffix: OpaquePointer) -> Bool
extension String {
/// Returns a Boolean value indicating whether the string begins with the
/// specified prefix.
///
/// The comparison is both case sensitive and Unicode safe. The
/// case-sensitive comparison will only match strings whose corresponding
/// characters have the same case.
///
/// let cafe = "Café du Monde"
///
/// // Case sensitive
/// print(cafe.hasPrefix("café"))
/// // Prints "false"
///
/// The Unicode-safe comparison matches Unicode scalar values rather than the
/// code points used to compose them. The example below uses two strings
/// with different forms of the `"é"` character---the first uses the composed
/// form and the second uses the decomposed form.
///
/// // Unicode safe
/// let composedCafe = "Café"
/// let decomposedCafe = "Cafe\u{0301}"
///
/// print(cafe.hasPrefix(composedCafe))
/// // Prints "true"
/// print(cafe.hasPrefix(decomposedCafe))
/// // Prints "true"
///
/// - Parameter prefix: A possible prefix to test against this string.
/// - Returns: `true` if the string begins with `prefix`, otherwise, `false`.
public func hasPrefix(_ prefix: String) -> Bool {
let selfCore = self._core
let prefixCore = prefix._core
let prefixCount = prefixCore.count
if prefixCount == 0 {
return true
}
if selfCore.hasContiguousStorage && prefixCore.hasContiguousStorage {
if selfCore.isASCII && prefixCore.isASCII {
// Prefix longer than self.
if prefixCount > selfCore.count {
return false
}
return Int(_swift_stdlib_memcmp(
selfCore.startASCII, prefixCore.startASCII, prefixCount)) == 0
}
let lhsStr = _NSContiguousString(selfCore)
let rhsStr = _NSContiguousString(prefixCore)
return lhsStr._unsafeWithNotEscapedSelfPointerPair(rhsStr) {
return _stdlib_NSStringHasPrefixNFDPointer($0, $1)
}
}
return _stdlib_NSStringHasPrefixNFD(
self._bridgeToObjectiveCImpl(), prefix._bridgeToObjectiveCImpl())
}
/// Returns a Boolean value indicating whether the string ends with the
/// specified suffix.
///
/// The comparison is both case sensitive and Unicode safe. The
/// case-sensitive comparison will only match strings whose corresponding
/// characters have the same case.
///
/// let plans = "Let's meet at the café"
///
/// // Case sensitive
/// print(plans.hasSuffix("Café"))
/// // Prints "false"
///
/// The Unicode-safe comparison matches Unicode scalar values rather than the
/// code points used to compose them. The example below uses two strings
/// with different forms of the `"é"` character---the first uses the composed
/// form and the second uses the decomposed form.
///
/// // Unicode safe
/// let composedCafe = "café"
/// let decomposedCafe = "cafe\u{0301}"
///
/// print(plans.hasSuffix(composedCafe))
/// // Prints "true"
/// print(plans.hasSuffix(decomposedCafe))
/// // Prints "true"
///
/// - Parameter suffix: A possible suffix to test against this string.
/// - Returns: `true` if the string ends with `suffix`, otherwise, `false`.
public func hasSuffix(_ suffix: String) -> Bool {
let selfCore = self._core
let suffixCore = suffix._core
let suffixCount = suffixCore.count
if suffixCount == 0 {
return true
}
if selfCore.hasContiguousStorage && suffixCore.hasContiguousStorage {
if selfCore.isASCII && suffixCore.isASCII {
// Suffix longer than self.
let selfCount = selfCore.count
if suffixCount > selfCount {
return false
}
return Int(_swift_stdlib_memcmp(
selfCore.startASCII + (selfCount - suffixCount),
suffixCore.startASCII, suffixCount)) == 0
}
let lhsStr = _NSContiguousString(selfCore)
let rhsStr = _NSContiguousString(suffixCore)
return lhsStr._unsafeWithNotEscapedSelfPointerPair(rhsStr) {
return _stdlib_NSStringHasSuffixNFDPointer($0, $1)
}
}
return _stdlib_NSStringHasSuffixNFD(
self._bridgeToObjectiveCImpl(), suffix._bridgeToObjectiveCImpl())
}
}
#else
// FIXME: Implement hasPrefix and hasSuffix without objc
// rdar://problem/18878343
#endif
// Conversions to string from other types.
extension String {
// FIXME: can't just use a default arg for radix below; instead we
// need these single-arg overloads <rdar://problem/17775455>
/// Creates a string representing the given value in base 10.
///
/// The following example converts the maximal `Int` value to a string and
/// prints its length:
///
/// let max = String(Int.max)
/// print("\(max) has \(max.utf16.count) digits.")
/// // Prints "9223372036854775807 has 19 digits."
public init<T : _SignedInteger>(_ v: T) {
self = _int64ToString(v.toIntMax())
}
/// Creates a string representing the given value in base 10.
///
/// The following example converts the maximal `UInt` value to a string and
/// prints its length:
///
/// let max = String(UInt.max)
/// print("\(max) has \(max.utf16.count) digits.")
/// // Prints "18446744073709551615 has 20 digits."
public init<T : UnsignedInteger>(_ v: T) {
self = _uint64ToString(v.toUIntMax())
}
/// Creates a string representing the given value in the specified base.
///
/// Numerals greater than 9 are represented as Roman letters. These letters
/// start with `"A"` if `uppercase` is `true`; otherwise, with `"a"`.
///
/// let v = 999_999
/// print(String(v, radix: 2))
/// // Prints "11110100001000111111"
///
/// print(String(v, radix: 16))
/// // Prints "f423f"
/// print(String(v, radix: 16, uppercase: true))
/// // Prints "F423F"
///
/// - Parameters:
/// - value: The value to convert to a string.
/// - radix: The base to use for the string representation. `radix` must be
/// at least 2 and at most 36.
/// - uppercase: Pass `true` to use uppercase letters to represent numerals
/// greater than 9, or `false` to use lowercase letters. The default is
/// `false`.
public init<T : _SignedInteger>(
_ value: T, radix: Int, uppercase: Bool = false
) {
_precondition(radix > 1, "Radix must be greater than 1")
self = _int64ToString(
value.toIntMax(), radix: Int64(radix), uppercase: uppercase)
}
/// Creates a string representing the given value in the specified base.
///
/// Numerals greater than 9 are represented as Roman letters. These letters
/// start with `"A"` if `uppercase` is `true`; otherwise, with `"a"`.
///
/// let v: UInt = 999_999
/// print(String(v, radix: 2))
/// // Prints "11110100001000111111"
///
/// print(String(v, radix: 16))
/// // Prints "f423f"
/// print(String(v, radix: 16, uppercase: true))
/// // Prints "F423F"
///
/// - Parameters:
/// - value: The value to convert to a string.
/// - radix: The base to use for the string representation. `radix` must be
/// at least 2 and at most 36.
/// - uppercase: Pass `true` to use uppercase letters to represent numerals
/// greater than 9, or `false` to use lowercase letters. The default is
/// `false`.
public init<T : UnsignedInteger>(
_ value: T, radix: Int, uppercase: Bool = false
) {
_precondition(radix > 1, "Radix must be greater than 1")
self = _uint64ToString(
value.toUIntMax(), radix: Int64(radix), uppercase: uppercase)
}
}
extension String {
/// Split the given string at the given delimiter character, returning the
/// strings before and after that character (neither includes the character
/// found) and a boolean value indicating whether the delimiter was found.
public func _splitFirst(separator delim: UnicodeScalar)
-> (before: String, after: String, wasFound : Bool)
{
let rng = unicodeScalars
for i in rng.indices {
if rng[i] == delim {
return (String(rng[rng.startIndex..<i]),
String(rng[rng.index(after: i)..<rng.endIndex]),
true)
}
}
return (self, "", false)
}
/// Split the given string at the first character for which the given
/// predicate returns true. Returns the string before that character, the
/// character that matches, the string after that character,
/// and a boolean value indicating whether any character was found.
public func _splitFirstIf(_ predicate: (UnicodeScalar) -> Bool)
-> (before: String, found: UnicodeScalar, after: String, wasFound: Bool)
{
let rng = unicodeScalars
for i in rng.indices {
if predicate(rng[i]) {
return (String(rng[rng.startIndex..<i]),
rng[i],
String(rng[rng.index(after: i)..<rng.endIndex]),
true)
}
}
return (self, "🎃", String(), false)
}
}
extension String {
@available(*, unavailable, message: "Renamed to init(repeating:count:) and reordered parameters")
public init(count: Int, repeatedValue c: Character) {
Builtin.unreachable()
}
@available(*, unavailable, message: "Renamed to init(repeating:count:) and reordered parameters")
public init(count: Int, repeatedValue c: UnicodeScalar) {
Builtin.unreachable()
}
}