Skip to content

Commit 7deb56c

Browse files
committed
testing iterators
1 parent 54e57c4 commit 7deb56c

File tree

4 files changed

+148
-1
lines changed

4 files changed

+148
-1
lines changed

stdlib/public/core/UTF8Span.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ extension UTF8Span {
199199
) throws(E) -> Result {
200200
try body(unsafeBaseAddress._ubp(0..<count))
201201
}
202+
203+
// TODO: withSpan or similar?
202204
}
203205

204206
// MARK: Internals

stdlib/public/core/UTF8SpanInternalHelpers.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ extension UnsafeRawPointer {
111111
_ i: Int,
112112
limitedBy end: Int
113113
) -> Bool {
114+
print(i)
115+
print(end)
114116
_internalInvariant(i >= 0 && i <= end)
115117
if i == 0 || i == end {
116118
return true

stdlib/public/core/UTF8SpanIterators.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
@available(SwiftStdlib 6.1, *)
22
extension UTF8Span {
3+
4+
public func _makeScalarIterator() -> ScalarIterator {
5+
.init(self)
6+
}
7+
38
/// Iterate the `Unicode.Scalar`s contents of a `UTF8Span`.
49
///
510
/// **TODO**: Examples
@@ -12,6 +17,7 @@ extension UTF8Span {
1217
/// **TODO**: private(set)?
1318
public var currentCodeUnitOffset: Int
1419

20+
// TODO: underscored init?
1521
public init(_ codeUnits: UTF8Span) {
1622
self.codeUnits = codeUnits
1723
self.currentCodeUnitOffset = 0
@@ -141,6 +147,11 @@ extension UTF8Span {
141147

142148
@available(SwiftStdlib 6.1, *)
143149
extension UTF8Span {
150+
151+
public func _makeCharacterIterator() -> CharacterIterator {
152+
.init(self)
153+
}
154+
144155
/// Iterate the `Character` contents of a `UTF8Span`.
145156
///
146157
/// **TODO**: Examples
@@ -177,6 +188,7 @@ extension UTF8Span {
177188
/// **TODO**: private(set)?
178189
public var currentCodeUnitOffset: Int
179190

191+
// TODO: underscored init?
180192
public init(_ span: UTF8Span) {
181193
self.codeUnits = span
182194
self.currentCodeUnitOffset = 0
@@ -212,7 +224,7 @@ extension UTF8Span {
212224
_internalInvariant(codeUnits.isScalarAligned(currentCodeUnitOffset))
213225
let (result, newPos) = codeUnits.unsafeBaseAddress._decodeCharacter(
214226
endingAt: currentCodeUnitOffset,
215-
limitedBy: 0)
227+
limitedBy: codeUnits.count)
216228
self.currentCodeUnitOffset = newPos
217229
return result
218230
}
@@ -274,11 +286,16 @@ extension UTF8Span {
274286
fatalError()
275287
}
276288
}
289+
277290
}
278291

279292
@available(SwiftStdlib 6.1, *)
280293
extension UTF8Span {
281294

295+
public func _makeGraphemeBreakIterator() -> GraphemeBreakIterator {
296+
.init(self)
297+
}
298+
282299
/// **QUESTION**: There are many ways we could expose this functionality and
283300
/// I'd like some help here. As written, the caller would be looking at
284301
/// `currentCodeUnitOffset` and `state` while `next()` / `previous
@@ -292,12 +309,14 @@ extension UTF8Span {
292309
public var currentCodeUnitOffset: Int
293310
public var state: Unicode.GraphemeBreakingState
294311

312+
// TODO: underscored init?
295313
public init(_ span: UTF8Span) {
296314
self.codeUnits = span
297315
self.currentCodeUnitOffset = 0
298316
self.state = .init()
299317
}
300318

319+
// TODO: underscored init?
301320
public init(_ span: UTF8Span, using state: Unicode.GraphemeBreakingState) {
302321
self.codeUnits = span
303322
self.currentCodeUnitOffset = 0
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// RUN: %target-run-stdlib-swift(-enable-experimental-feature Span) -enable-experimental-feature Span %S/Inputs/
2+
3+
// REQUIRES: executable_test
4+
5+
import Swift
6+
import StdlibUnittest
7+
8+
var suite = TestSuite("UTF8SpanIterator")
9+
defer { runAllTests() }
10+
11+
@available(SwiftStdlib 6.1, *)
12+
extension Array {
13+
func withSpan<R>(_ f: (Span<Element>) throws -> R) rethrows -> R {
14+
try self.withUnsafeBufferPointer {
15+
try f(Span(_unsafeElements: $0))
16+
}
17+
}
18+
}
19+
20+
@available(SwiftStdlib 6.1, *)
21+
extension UTF8Span {
22+
func withSpan<R>(_ f: (Span<UInt8>) throws -> R) rethrows -> R {
23+
try self.withUnsafeBufferPointer {
24+
try f(Span(_unsafeElements: $0))
25+
}
26+
}
27+
}
28+
29+
//
30+
@available(SwiftStdlib 6.1, *)
31+
struct ContentEquivalenceTestCase {
32+
var str: String
33+
var loc: SourceLocStack
34+
35+
func withUTF8Span<R>(_ f: (UTF8Span) throws -> R) rethrows -> R {
36+
try Array(str.utf8).withSpan { span in
37+
try f(try! UTF8Span(_validating: span))
38+
}
39+
}
40+
41+
func testBytes() {
42+
let otherBytes = Array((str+"abc").utf8)
43+
44+
withUTF8Span { utf8Span in
45+
utf8Span.withUnsafeBufferPointer {
46+
expectEqualSequence(str.utf8, $0, stackTrace: loc)
47+
}
48+
}
49+
50+
// NOTE: There's a slight jarring due to not having the same
51+
// iterators for code units
52+
}
53+
54+
func testScalars() {
55+
withUTF8Span { utf8Span in
56+
// Test forwards
57+
var utf8SpanIter = utf8Span._makeScalarIterator()
58+
var stringIter = str.unicodeScalars.makeIterator()
59+
while let scalar = utf8SpanIter.next() {
60+
expectEqual(scalar, stringIter.next(), stackTrace: loc)
61+
}
62+
expectNil(stringIter.next(), stackTrace: loc)
63+
64+
// Test backwards
65+
var stringRevIter = str.unicodeScalars.reversed().makeIterator()
66+
while let scalar = utf8SpanIter.previous() {
67+
expectEqual(scalar, stringRevIter.next(), stackTrace: loc)
68+
}
69+
expectNil(stringRevIter.next(), stackTrace: loc)
70+
71+
}
72+
}
73+
74+
func testCharacters() {
75+
withUTF8Span { utf8Span in
76+
// Test forwards
77+
var utf8SpanIter = utf8Span._makeCharacterIterator()
78+
var stringIter = str.makeIterator()
79+
while let char = utf8SpanIter.next() {
80+
expectEqual(char, stringIter.next(), stackTrace: loc)
81+
}
82+
expectNil(stringIter.next(), stackTrace: loc)
83+
84+
// Test backwards
85+
var stringRevIter = str.reversed().makeIterator()
86+
while let char = utf8SpanIter.previous() {
87+
expectEqual(char, stringRevIter.next(), stackTrace: loc)
88+
}
89+
expectNil(stringRevIter.next(), stackTrace: loc)
90+
}
91+
}
92+
93+
func run() {
94+
testBytes()
95+
testScalars()
96+
testCharacters()
97+
}
98+
99+
}
100+
101+
if #available(SwiftStdlib 6.1, *) {
102+
suite.test("UTF8Span/iterators") {
103+
func test(
104+
_ s: String,
105+
file: String = #file,
106+
line: UInt = #line
107+
) {
108+
print("testing: \(s)")
109+
let t = ContentEquivalenceTestCase(
110+
str: s, loc: .init(SourceLoc(file, line)))
111+
t.run()
112+
}
113+
114+
test("abc")
115+
test("abcde\u{301}")
116+
test("abcde\u{301}")
117+
test("abéÏ𓀀")
118+
test("012345678901234567890")
119+
test("abéÏ012345678901234567890𓀀")
120+
test("😀😃🤢🤮👩🏿‍🎤🧛🏻‍♂️🧛🏻‍♂️👩‍👩‍👦‍👦")
121+
test("")
122+
}
123+
}
124+

0 commit comments

Comments
 (0)