mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
e6e4bd6056
Add support for UTF8Span Also, refactor validation and grapheme breaking
280 lines
8.1 KiB
Swift
280 lines
8.1 KiB
Swift
// RUN: %target-run-stdlib-swift(-enable-experimental-feature LifetimeDependence) %S/Inputs/
|
|
// REQUIRES: swift_feature_LifetimeDependence
|
|
// REQUIRES: executable_test
|
|
|
|
import Swift
|
|
import StdlibUnittest
|
|
|
|
var suite = TestSuite("UTF8SpanIterator")
|
|
defer { runAllTests() }
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension Array {
|
|
func withSpan<R>(_ f: (Span<Element>) throws -> R) rethrows -> R {
|
|
try self.withUnsafeBufferPointer {
|
|
try f(Span(_unsafeElements: $0))
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension UTF8Span {
|
|
func withSpan<R>(_ f: (Span<UInt8>) throws -> R) rethrows -> R {
|
|
try self._withUnsafeBufferPointer {
|
|
try f(Span(_unsafeElements: $0))
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
@available(SwiftStdlib 6.2, *)
|
|
struct ContentEquivalenceTestCase {
|
|
var str: String
|
|
var loc: SourceLocStack
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension ContentEquivalenceTestCase {
|
|
func expectStart(
|
|
_ scalars: inout UTF8Span.UnicodeScalarIterator
|
|
) {
|
|
let firstScalar = str.unicodeScalars.first
|
|
expectEqual(0, scalars.currentCodeUnitOffset, stackTrace: loc)
|
|
expectNil(scalars.previous(), stackTrace: loc)
|
|
expectEqual(firstScalar, scalars.next(), stackTrace: loc)
|
|
expectEqual(firstScalar, scalars.previous(), stackTrace: loc)
|
|
expectNil(scalars.previous(), stackTrace: loc)
|
|
}
|
|
|
|
func expectEnd(
|
|
_ scalars: inout UTF8Span.UnicodeScalarIterator
|
|
) {
|
|
let lastScalar = str.unicodeScalars.last
|
|
expectEqual(scalars.currentCodeUnitOffset, scalars.codeUnits.count, stackTrace: loc)
|
|
expectNil(scalars.next(), stackTrace: loc)
|
|
expectEqual(lastScalar, scalars.previous(), stackTrace: loc)
|
|
expectEqual(lastScalar, scalars.next(), stackTrace: loc)
|
|
expectNil(scalars.next(), stackTrace: loc)
|
|
}
|
|
|
|
func expectStart(
|
|
_ chars: inout UTF8Span.CharacterIterator
|
|
) {
|
|
let firstChar = str.first
|
|
expectEqual(0, chars.currentCodeUnitOffset, stackTrace: loc)
|
|
expectNil(chars.previous(), stackTrace: loc)
|
|
expectEqual(firstChar, chars.next(), stackTrace: loc)
|
|
expectEqual(firstChar, chars.previous(), stackTrace: loc)
|
|
expectNil(chars.previous(), stackTrace: loc)
|
|
}
|
|
|
|
func expectEnd(
|
|
_ chars: inout UTF8Span.CharacterIterator
|
|
) {
|
|
let lastChar = str.last
|
|
expectEqual(chars.currentCodeUnitOffset, chars.codeUnits.count, stackTrace: loc)
|
|
expectNil(chars.next(), stackTrace: loc)
|
|
expectEqual(lastChar, chars.previous(), stackTrace: loc)
|
|
expectEqual(lastChar, chars.next(), stackTrace: loc)
|
|
expectNil(chars.next(), stackTrace: loc)
|
|
}
|
|
|
|
|
|
func withUTF8Span<R>(_ f: (UTF8Span) throws -> R) rethrows -> R {
|
|
try Array(str.utf8).withSpan { span in
|
|
try f(try! UTF8Span(validating: span))
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension ContentEquivalenceTestCase {
|
|
func testBytes() {
|
|
let otherBytes = Array((str+"abc").utf8)
|
|
|
|
withUTF8Span { utf8Span in
|
|
utf8Span._withUnsafeBufferPointer {
|
|
expectEqualSequence(str.utf8, $0, stackTrace: loc)
|
|
}
|
|
}
|
|
|
|
// NOTE: There's a slight jarring due to not having the same
|
|
// iterators for code units
|
|
}
|
|
|
|
func testScalars() {
|
|
withUTF8Span { utf8Span in
|
|
// Test forwards
|
|
var utf8SpanIter = utf8Span.makeUnicodeScalarIterator()
|
|
var stringIter = str.unicodeScalars.makeIterator()
|
|
while let scalar = utf8SpanIter.next() {
|
|
expectEqual(scalar, stringIter.next(), stackTrace: loc)
|
|
}
|
|
expectNil(stringIter.next(), stackTrace: loc)
|
|
expectEnd(&utf8SpanIter)
|
|
|
|
// Test backwards
|
|
var stringRevIter = str.unicodeScalars.reversed().makeIterator()
|
|
while let scalar = utf8SpanIter.previous() {
|
|
expectEqual(scalar, stringRevIter.next(), stackTrace: loc)
|
|
}
|
|
expectNil(stringRevIter.next(), stackTrace: loc)
|
|
expectStart(&utf8SpanIter)
|
|
|
|
let numElements = str.unicodeScalars.count
|
|
let lastElement = str.unicodeScalars.last
|
|
let firstElement = str.unicodeScalars.first
|
|
|
|
expectEqual(numElements, utf8SpanIter.skipForward(by: Int.max))
|
|
expectEnd(&utf8SpanIter)
|
|
expectEqual(numElements, utf8SpanIter.skipBack(by: Int.max))
|
|
expectStart(&utf8SpanIter)
|
|
expectEqual(numElements, utf8SpanIter.skipForward(by: numElements))
|
|
expectEnd(&utf8SpanIter)
|
|
expectEqual(numElements, utf8SpanIter.skipBack(by: numElements))
|
|
expectStart(&utf8SpanIter)
|
|
|
|
if numElements > 0 {
|
|
expectStart(&utf8SpanIter)
|
|
expectEqual(numElements-1, utf8SpanIter.skipForward(by: numElements-1))
|
|
expectEqual(lastElement, utf8SpanIter.next())
|
|
expectEnd(&utf8SpanIter)
|
|
|
|
expectEqual(numElements-1, utf8SpanIter.skipBack(by: numElements-1))
|
|
expectEqual(firstElement, utf8SpanIter.previous())
|
|
expectStart(&utf8SpanIter)
|
|
}
|
|
|
|
// TODO: test reset variants
|
|
// TODO: test prefix/suffix
|
|
|
|
}
|
|
}
|
|
|
|
func testCharacters() {
|
|
withUTF8Span { utf8Span in
|
|
// Test forwards
|
|
var utf8SpanIter = utf8Span.makeCharacterIterator()
|
|
var stringIter = str.makeIterator()
|
|
while let char = utf8SpanIter.next() {
|
|
expectEqual(char, stringIter.next(), stackTrace: loc)
|
|
}
|
|
expectNil(stringIter.next(), stackTrace: loc)
|
|
expectEnd(&utf8SpanIter)
|
|
|
|
// Test backwards
|
|
var stringRevIter = str.reversed().makeIterator()
|
|
while let char = utf8SpanIter.previous() {
|
|
expectEqual(char, stringRevIter.next(), stackTrace: loc)
|
|
}
|
|
expectNil(stringRevIter.next(), stackTrace: loc)
|
|
expectStart(&utf8SpanIter)
|
|
|
|
let numElements = str.count
|
|
let lastElement = str.last
|
|
let firstElement = str.first
|
|
|
|
expectEqual(numElements, utf8SpanIter.skipForward(by: Int.max))
|
|
expectEnd(&utf8SpanIter)
|
|
expectEqual(numElements, utf8SpanIter.skipBack(by: Int.max))
|
|
expectStart(&utf8SpanIter)
|
|
expectEqual(numElements, utf8SpanIter.skipForward(by: numElements))
|
|
expectEnd(&utf8SpanIter)
|
|
expectEqual(numElements, utf8SpanIter.skipBack(by: numElements))
|
|
expectStart(&utf8SpanIter)
|
|
|
|
if numElements > 0 {
|
|
expectStart(&utf8SpanIter)
|
|
expectEqual(numElements-1, utf8SpanIter.skipForward(by: numElements-1))
|
|
expectEqual(lastElement, utf8SpanIter.next())
|
|
expectEnd(&utf8SpanIter)
|
|
|
|
expectEqual(numElements-1, utf8SpanIter.skipBack(by: numElements-1))
|
|
expectEqual(firstElement, utf8SpanIter.previous())
|
|
expectStart(&utf8SpanIter)
|
|
}
|
|
|
|
// TODO: test reset variants
|
|
// TODO: test prefix/suffix
|
|
}
|
|
}
|
|
|
|
func run() {
|
|
testBytes()
|
|
testScalars()
|
|
testCharacters()
|
|
|
|
// TODO: test grapheme break iterator
|
|
}
|
|
|
|
}
|
|
|
|
if #available(SwiftStdlib 6.2, *) {
|
|
suite.test("UTF8Span/iterators") {
|
|
func test(
|
|
_ s: String,
|
|
file: String = #file,
|
|
line: UInt = #line
|
|
) {
|
|
// print("testing: \(s)")
|
|
let t = ContentEquivalenceTestCase(
|
|
str: s, loc: .init(SourceLoc(file, line)))
|
|
t.run()
|
|
}
|
|
|
|
test("")
|
|
test("a")
|
|
test("á")
|
|
test("a\u{301}")
|
|
test("🧟♀️")
|
|
test("abc")
|
|
test("abcde\u{301}")
|
|
test("abéÏ𓀀")
|
|
test("012345678901234567890")
|
|
test("abéÏ012345678901234567890𓀀")
|
|
test("😀😃🤢🤮👩🏿🎤🧛🏻♂️🧛🏻♂️👩👩👦👦")
|
|
test("defghijklmnopqrstuvwxyz")
|
|
test("ab🧟♀️de\u{301}bytés")
|
|
test("ab🧟♀️de\u{301}🧟♀️")
|
|
test("ab🧟♀️de🧟♀️\u{301}")
|
|
}
|
|
}
|
|
|
|
// @available(SwiftStdlib 6.2, *)
|
|
// extension UTF8Span {
|
|
// func splitOffASCIIPrefix() -> (UTF8Span, UTF8Span) {
|
|
// if isKnownASCII {
|
|
// return (self, .init())
|
|
// }
|
|
// var splitPoint = 0
|
|
// while splitPoint < codeUnits.count && codeUnits[unchecked: split] < 0x80 {
|
|
// splitPoint += 1
|
|
// }
|
|
|
|
// }
|
|
// }
|
|
|
|
if #available(SwiftStdlib 6.2, *) {
|
|
suite.test("UTF8Span/whatever") {
|
|
// var badURLBytes: [UInt8] = []
|
|
// badURLBytes.append(contentsOf: "http://servername/scripts/..".utf8)
|
|
|
|
// // Invalid overlong encoding of "/"
|
|
// badURLBytes.append(contentsOf: [0xC0, 0xAF])
|
|
|
|
// badURLBytes.append(contentsOf: "../winnt/system32/cmd.exe".utf8)
|
|
|
|
// // try! UTF8Span(validating: badURLBytes.span)
|
|
|
|
// badURLBytes.withSpan {
|
|
// try! UTF8Span(validating: $0)
|
|
// }
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|