//===--- SpanTests.swift --------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2024 - 2025 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 // //===----------------------------------------------------------------------===// // RUN: %target-run-stdlib-swift // REQUIRES: executable_test import StdlibUnittest var suite = TestSuite("Span Tests") defer { runAllTests() } @available(SwiftStdlib 6.2, *) extension Span where Element: Equatable { /// Returns a Boolean value indicating whether this and another span /// contain equal elements in the same order. /// /// - Parameters: /// - other: A span to compare to this one. /// - Returns: `true` if this sequence and `other` contain equivalent items, /// using `areEquivalent` as the equivalence test; otherwise, `false.` /// /// - Complexity: O(*m*), where *m* is the lesser of the length of the /// sequence and the length of `other`. @_alwaysEmitIntoClient public func _elementsEqual(_ other: Self) -> Bool { guard count == other.count else { return false } if count == 0 { return true } //FIXME: This could be short-cut // with a layout constraint where stride equals size, // as long as there is at most 1 unused bit pattern. // if Element is BitwiseEquatable { // return _swift_stdlib_memcmp(lhs.baseAddress, rhs.baseAddress, count) == 0 // } for o in 0..) -> Bool { let equal = other.withContiguousStorageIfAvailable { _elementsEqual(Span(_unsafeElements: $0)) } if let equal { return equal } guard count == other.count else { return false } if count == 0 { return true } return _elementsEqual(AnySequence(other)) } /// Returns a Boolean value indicating whether this span and a Sequence /// contain equal elements in the same order. /// /// - Parameters: /// - other: A Sequence to compare to this span. /// - Returns: `true` if this sequence and `other` contain equivalent items, /// using `areEquivalent` as the equivalence test; otherwise, `false.` /// /// - Complexity: O(*m*), where *m* is the lesser of the length of the /// sequence and the length of `other`. @_alwaysEmitIntoClient public func _elementsEqual(_ other: some Sequence) -> Bool { var offset = 0 for otherElement in other { if offset >= count { return false } if self[unchecked: offset] != otherElement { return false } offset += 1 } return offset == count } } suite.test("Initialize with ordinary element") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let capacity = 4 var s = (0..(_unsafeBytes: $0) expectEqual(b.count, capacity) let r = Span(_unsafeBytes: $0) expectEqual(r.count, capacity*MemoryLayout.stride) let p = $0.baseAddress! let span = Span( _unsafeStart: p, byteCount: capacity*MemoryLayout.stride ) expectEqual(span.count, capacity) } a.withUnsafeMutableBytes { let b = Span(_unsafeBytes: $0) expectEqual(b.count, capacity) let p = $0.baseAddress! let span = Span( _unsafeStart: p, byteCount: capacity*MemoryLayout.stride ) expectEqual(span.count, capacity) } } suite.test("isEmpty") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let capacity = 4 let a = Array(0...stride) } } suite.test("indices property") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let capacity = 4 let a = Array(0..(unsafeUninitializedCapacity: capacity) { for i in $0.indices { $0.initializeElement(at: i, to: i) } $1 = $0.count } a.withUnsafeBufferPointer { let span = Span(_unsafeElements: $0) expectEqual(span._elementsEqual(span.extracting(first: 1)), false) expectEqual(span.extracting(0..<0)._elementsEqual(span.extracting(last: 0)), true) expectEqual(span._elementsEqual(span), true) expectEqual(span.extracting(0..<3)._elementsEqual(span.extracting(last: 3)), false) let copy = span.withUnsafeBufferPointer(Array.init) copy.withUnsafeBufferPointer { let spanOfCopy = Span(_unsafeElements: $0) expectTrue(span._elementsEqual(spanOfCopy)) } } } suite.test("_elementsEqual(_: some Collection)") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let capacity = 4 let a = Array(0..(start: nil, count: 0) let span = Span(_unsafeElements: b) expectEqual(span.count, b.count) expectEqual(span.extracting(first: 1).count, b.count) expectEqual(span.extracting(droppingLast: 1).count, b.count) } } suite.test("suffix extracting() functions") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let capacity = 4 let a = Array(0..(_unsafeElements: $0) expectEqual(span.count, capacity) expectEqual(span.extracting(last: capacity)[0], 0) expectEqual(span.extracting(last: capacity-1)[0], 1) expectEqual(span.extracting(last: 1)[0], capacity-1) expectEqual(span.extracting(droppingFirst: capacity).count, 0) expectEqual(span.extracting(droppingFirst: 1)[0], 1) } do { let b = UnsafeBufferPointer(start: nil, count: 0) let span = Span(_unsafeElements: b) expectEqual(span.count, b.count) expectEqual(span.extracting(last: 1).count, b.count) expectEqual(span.extracting(droppingFirst: 1).count, b.count) } } suite.test("withUnsafeBufferPointer") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let capacity: UInt8 = 64 let a = Array(0...allocate(capacity: 8) _ = b.initialize(fromContentsOf: 0..<8) defer { b.deallocate() } let span = Span(_unsafeElements: b) let pre = span.extracting(first: 6) let suf = span.extracting(last: 6) expectFalse( pre.isIdentical(to: suf) ) expectFalse( pre.isIdentical(to: span) ) expectTrue( pre.extracting(last: 4).isIdentical(to: suf.extracting(first: 4)) ) } suite.test("indices(of:)") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let b = UnsafeMutableBufferPointer.allocate(capacity: 8) _ = b.initialize(fromContentsOf: 0..<8) defer { b.deallocate() } let span = Span(_unsafeElements: b) let subSpan1 = span.extracting(first: 6) let subSpan2 = span.extracting(last: 6) let emptySpan = span.extracting(first: 0) /* This isn't relevant until we can support unaligned spans let unalignedSpan = RawSpan(_unsafeSpan: span) .extracting(droppingFirst: 6) .extracting(droppingLast: 2) .unsafeView(as: Int.self) */ let nilBuffer = UnsafeBufferPointer(start: nil, count: 0) let nilSpan = Span(_unsafeElements: nilBuffer) var bounds: Range? bounds = span.indices(of: subSpan1) expectEqual(bounds, span.indices.prefix(6)) bounds = span.indices(of: subSpan2) expectEqual(bounds, span.indices.suffix(6)) bounds = subSpan2.indices(of: subSpan1) expectNil(bounds) bounds = subSpan1.indices(of: subSpan2) expectNil(bounds) bounds = subSpan2.indices(of: span) expectNil(bounds) bounds = nilSpan.indices(of: emptySpan) expectNil(bounds) bounds = span.indices(of: nilSpan) expectNil(bounds) bounds = nilSpan.indices(of: nilSpan) expectEqual(bounds, 0..<0) } suite.test("initialize from raw memory") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" )) .code { guard #available(SwiftStdlib 6.2, *) else { return } let b = UnsafeMutableRawBufferPointer.allocate(byteCount: 64, alignment: 8) defer { b.deallocate() } let initialized = b.initializeMemory(as: UInt8.self, fromContentsOf: 0..<64) expectEqual(initialized.count, 64) defer { initialized.deinitialize() } func test(_ span: Span) -> T { span[0] } // let span = Span(_unsafeBytes: b.dropFirst().dropLast(7)) let suffix = b.dropFirst(4) let span = Span(_unsafeBytes: suffix) let first = test(span) expectEqual(first, 0x07060504) } private func send(_: some Sendable & ~Escapable) {} private struct NCSendable: ~Copyable, Sendable {} suite.test("Span Sendability") .require(.stdlib_6_2).code { guard #available(SwiftStdlib 6.2, *) else { return } let buffer = UnsafeMutableBufferPointer.allocate(capacity: 1) defer { buffer.deallocate() } buffer.initializeElement(at: 0, to: NCSendable()) defer { buffer.deinitialize() } let span = Span(_unsafeElements: buffer) send(span) }