-
Notifications
You must be signed in to change notification settings - Fork 10.3k
/
StringLegacy.swift
197 lines (186 loc) · 7.22 KB
/
StringLegacy.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
import SwiftShims
extension String {
/// Creates a new string representing the given string repeated the specified
/// number of times.
///
/// For example, you can use this initializer to create a string with ten
/// `"ab"` strings in a row.
///
/// let s = String(repeating: "ab", count: 10)
/// print(s)
/// // Prints "abababababababababab"
///
/// - 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) {
_precondition(count >= 0, "Negative count not allowed")
guard count > 1 else {
self = count == 0 ? "" : repeatedValue
return
}
// TODO(String performance): We can directly call appendInPlace
var result = String()
result.reserveCapacity(repeatedValue._guts.count &* count)
for _ in 0..<count {
result += repeatedValue
}
self = result
}
/// A Boolean value indicating whether a string has no characters.
@inlinable
public var isEmpty: Bool {
@inline(__always) get { return _guts.isEmpty }
}
}
extension StringProtocol {
/// 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 extended grapheme clusters
/// 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`.
@inlinable
public func hasPrefix<Prefix: StringProtocol>(_ prefix: Prefix) -> Bool {
return self.starts(with: prefix)
}
/// 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 extended grapheme clusters
/// 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`.
@inlinable
public func hasSuffix<Suffix: StringProtocol>(_ suffix: Suffix) -> Bool {
return self.reversed().starts(with: suffix.reversed())
}
}
extension String {
public func hasPrefix(_ prefix: String) -> Bool {
if _fastPath(self._guts.isNFCFastUTF8 && prefix._guts.isNFCFastUTF8) {
guard prefix._guts.count <= self._guts.count else { return false }
let isPrefix = prefix._guts.withFastUTF8 { nfcPrefix in
let prefixEnd = nfcPrefix.count
return self._guts.withFastUTF8(range: 0..<prefixEnd) { nfcSlicedSelf in
return _binaryCompare(nfcSlicedSelf, nfcPrefix) == 0
}
}
let endIndex = Index(_encodedOffset: prefix._guts.count)
// In addition to a byte comparison check, we also need to check that
// the prefix ends on a grapheme cluster boundary of the String
return isPrefix && self._guts.isOnGraphemeClusterBoundary(endIndex)
}
return starts(with: prefix)
}
public func hasSuffix(_ suffix: String) -> Bool {
if _fastPath(self._guts.isNFCFastUTF8 && suffix._guts.isNFCFastUTF8) {
let suffixStart = self._guts.count - suffix._guts.count
guard suffixStart >= 0 else { return false }
let isSuffix = suffix._guts.withFastUTF8 { nfcSuffix in
return self._guts.withFastUTF8(range: suffixStart..<self._guts.count) {
nfcSlicedSelf in return _binaryCompare(nfcSlicedSelf, nfcSuffix) == 0
}
}
let startIndex = Index(_encodedOffset: suffixStart)
// In addition to a byte comparison check, we also need to check that
// the suffix starts on a grapheme cluster boundary of the String
return isSuffix && self._guts.isOnGraphemeClusterBoundary(startIndex)
}
return self.reversed().starts(with: suffix.reversed())
}
}
// Conversions to string from other types.
extension String {
/// Creates a string representing the given value in base 10, or some other
/// specified base.
///
/// The following example converts the maximal `Int` value to a string and
/// prints its length:
///
/// let max = String(Int.max)
/// print("\(max) has \(max.count) digits.")
/// // Prints "9223372036854775807 has 19 digits."
///
/// 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. The default is 10.
/// - 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: BinaryInteger>(
_ value: T, radix: Int = 10, uppercase: Bool = false
) {
self = value._description(radix: radix, uppercase: uppercase)
}
}