Skip to content

Commit c0623c4

Browse files
authored
Merge pull request #9722 from apple/stringprotocol-interchange
2 parents 2428807 + d6fee05 commit c0623c4

File tree

6 files changed

+128
-3
lines changed

6 files changed

+128
-3
lines changed

stdlib/public/core/String.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,42 @@ public protocol StringProtocol
110110
) rethrows -> Result
111111
}
112112

113+
/// A protocol that provides fast access to a known representation of String.
114+
///
115+
/// Can be used to specialize generic functions that would otherwise end up
116+
/// doing grapheme breaking to vend individual characters.
117+
internal protocol _SwiftStringView {
118+
/// A `String`, having the same contents as `self`, that may be unsuitable for
119+
/// long-term storage.
120+
var _ephemeralContent : String { get }
121+
122+
/// A `String`, having the same contents as `self`, that is suitable for
123+
/// long-term storage.
124+
var _persistentContent : String { get }
125+
}
126+
127+
extension _SwiftStringView {
128+
var _ephemeralContent : String { return _persistentContent }
129+
}
130+
131+
extension StringProtocol {
132+
internal var _ephemeralString : String {
133+
let s0 = self as? _SwiftStringView
134+
if _fastPath(s0 != nil), let s1 = s0 { return s1._ephemeralContent }
135+
return String(String.CharacterView(self))
136+
}
137+
138+
internal var _persistentString : String {
139+
let s0 = self as? _SwiftStringView
140+
if _fastPath(s0 != nil), let s1 = s0 { return s1._persistentContent }
141+
return String(String.CharacterView(self))
142+
}
143+
}
144+
145+
extension String : _SwiftStringView {
146+
var _persistentContent : String { return characters._persistentContent }
147+
}
148+
113149
/// Call body with a pointer to zero-terminated sequence of
114150
/// `TargetEncoding.CodeUnit` representing the same string as `source`, when
115151
/// `source` is interpreted as being encoded with `SourceEncoding`.

stdlib/public/core/StringCharacterView.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,20 @@ extension String {
153153
}
154154
}
155155

156+
extension String.CharacterView : _SwiftStringView {
157+
var _persistentContent : String {
158+
// FIXME: we might want to make sure our _StringCore isn't somehow a slice
159+
// of some larger storage before blindly wrapping/returning it as
160+
// persistent. That said, if current benchmarks are measuring these cases,
161+
// we might end up regressing something by copying the storage. For now,
162+
// assume we are not a slice; we can come back and measure the effects of
163+
// this fix later. If we make the fix we should use the line below as an
164+
// implementation of _ephemeralContent
165+
return String(self._core)
166+
}
167+
}
168+
169+
156170
/// `String.CharacterView` is a collection of `Character`.
157171
extension String.CharacterView : BidirectionalCollection {
158172
internal typealias UnicodeScalarView = String.UnicodeScalarView
@@ -658,9 +672,15 @@ extension String.CharacterView : RangeReplaceableCollection {
658672
///
659673
/// - Parameter characters: A sequence of characters.
660674
public init<S : Sequence>(_ characters: S)
661-
where S.Element == Character {
662-
self = String.CharacterView()
663-
self.append(contentsOf: characters)
675+
where S.Element == Character {
676+
let v0 = characters as? _SwiftStringView
677+
if _fastPath(v0 != nil), let v = v0 {
678+
self = v._persistentContent.characters
679+
}
680+
else {
681+
self = String.CharacterView()
682+
self.append(contentsOf: characters)
683+
}
664684
}
665685
}
666686

stdlib/public/core/StringUTF16.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,11 @@ extension String {
352352
public typealias UTF16Index = UTF16View.Index
353353
}
354354

355+
extension String.UTF16View : _SwiftStringView {
356+
var _ephemeralContent : String { return _persistentContent }
357+
var _persistentContent : String { return String(self._core) }
358+
}
359+
355360
extension String.UTF16View.Index : Comparable {
356361
// FIXME: swift-3-indexing-model: add complete set of forwards for Comparable
357362
// assuming String.UTF8View.Index continues to exist

stdlib/public/core/StringUTF8.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ extension String {
454454
public typealias UTF8Index = UTF8View.Index
455455
}
456456

457+
extension String.UTF8View : _SwiftStringView {
458+
var _persistentContent : String { return String(self._core) }
459+
}
460+
461+
457462
extension String.UTF8View.Index : Comparable {
458463
// FIXME: swift-3-indexing-model: add complete set of forwards for Comparable
459464
// assuming String.UTF8View.Index continues to exist

stdlib/public/core/StringUnicodeScalarView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ extension String {
326326
public typealias UnicodeScalarIndex = UnicodeScalarView.Index
327327
}
328328

329+
extension String.UnicodeScalarView : _SwiftStringView {
330+
var _persistentContent : String { return String(_core) }
331+
}
332+
329333
extension String {
330334
/// The string's value represented as a collection of Unicode scalar values.
331335
public var unicodeScalars: UnicodeScalarView {

stdlib/public/core/Substring.swift.gyb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,31 @@ public struct Substring : StringProtocol {
257257
}
258258
}
259259

260+
extension Substring : _SwiftStringView {
261+
var _persistentContent : String {
262+
let wholeCore = _slice._base._core
263+
let native = wholeCore.nativeBuffer
264+
if _fastPath(native != nil), let n = native {
265+
if _fastPath(
266+
n.start == wholeCore._baseAddress && n.usedCount == wholeCore.count) {
267+
return String(wholeCore)
268+
}
269+
}
270+
var r = String()
271+
r._core.append(_ephemeralContent._core)
272+
_sanityCheck(r._core._owner !== _ephemeralContent._core._owner)
273+
return r
274+
}
275+
276+
var _ephemeralContent : String {
277+
let wholeCore = _slice._base._core
278+
let subCore : _StringCore = wholeCore[
279+
startIndex._base._position..<endIndex._base._position]
280+
// check that we haven't allocated a new buffer for the result
281+
_sanityCheck(subCore._owner === wholeCore._owner)
282+
return String(subCore)
283+
}
284+
}
260285

261286
extension Substring : CustomReflectable {
262287
public var customMirror: Mirror {
@@ -361,6 +386,36 @@ extension Substring {
361386

362387
#endif
363388

389+
extension Substring : RangeReplaceableCollection {
390+
public init<S : Sequence>(_ elements: S)
391+
where S.Element == Character {
392+
let e0 = elements as? _SwiftStringView
393+
if _fastPath(e0 != nil), let e = e0 {
394+
self = e._ephemeralContent[...]
395+
}
396+
else {
397+
self = String(elements)[...]
398+
}
399+
}
400+
401+
public mutating func append<S : Sequence>(contentsOf elements: S)
402+
where S.Element == Character {
403+
var c = self._ephemeralContent._core
404+
self = Substring()
405+
406+
let e0 = elements as? _SwiftStringView
407+
if _fastPath(e0 != nil), let e1 = e0 {
408+
c.append(contentsOf: e1._ephemeralContent.utf16)
409+
self = String(c)[...]
410+
}
411+
else {
412+
var s = String(c)
413+
s.append(contentsOf: elements)
414+
self = s[...]
415+
}
416+
}
417+
}
418+
364419
extension Substring {
365420
public func lowercased() -> String {
366421
return String(self).lowercased()

0 commit comments

Comments
 (0)