Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions Sources/ContainersPreview/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,31 @@ endif()
target_sources(${module_name} PRIVATE
"Extensions/OutputSpan+Extras.swift"
"Extensions/TemporaryAllocation.swift"
"Protocols/Container/BidirectionalContainer.swift"
"Protocols/Container/Container.swift"
"Protocols/Container/ContainerAlgorithms.swift"
"Protocols/Container/DynamicContainer.swift"
"Protocols/Container/MutableContainer.swift"
"Protocols/Container/PermutableContainer.swift"
"Protocols/Container/RandomAccessContainer.swift"
"Protocols/Container/RangeExpression2.swift"
"Protocols/Container/RangeReplaceableContainer.swift"
"Protocols/BorrowingIteratorProtocol.swift"
"Protocols/BorrowingIteratorProtocol+ElementsEqual.swift"
"Protocols/BorrowingIteratorProtocol+Map.swift"
"Protocols/BorrowingIteratorProtocol+Reduce.swift"
"Protocols/BorrowingIteratorProtocol+SpanwiseZip.swift"
"Protocols/BorrowingSequence.swift"
"Protocols/BorrowingSequence+Standard Conformances.swift"
"Protocols/BorrowingSequence+Utilities.swift"
"Protocols/Container.swift"
"Protocols/ContainerAlgorithms.swift"
"Protocols/Drain.swift"
"Protocols/Drain+Map.swift"
"Protocols/Drain+Reduce.swift"
"Protocols/Drain.swift"
"Protocols/Producer.swift"
"Protocols/Producer+Map.swift"
"Protocols/Producer+Reduce.swift"
"Protocols/Producer+Collect.swift"
"Types/Borrow.swift"
"Types/Box.swift"
"Types/Inout.swift"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Collections open source project
//
// Copyright (c) 2024 - 2026 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
//
// SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
//
//===----------------------------------------------------------------------===//

#if !COLLECTIONS_SINGLE_MODULE
import InternalCollectionsUtilities
#endif

#if compiler(>=6.2) && COLLECTIONS_UNSTABLE_CONTAINERS_PREVIEW

@available(SwiftStdlib 5.0, *)
extension BorrowingSequence where Self: ~Copyable & ~Escapable, Element: Equatable {
@inlinable
package func _elementsEqual<
Other: BorrowingSequence<Element> & ~Copyable & ~Escapable
>(
_ other: borrowing Other,
) -> Bool {
let it1 = self.makeBorrowingIterator()
let it2 = other.makeBorrowingIterator()
return it1.elementsEqual(it2)
}
}

@available(SwiftStdlib 5.0, *)
extension BorrowingSequence where Self: ~Copyable & ~Escapable {
/// Returns a Boolean value indicating whether two borrowing sequences contain
/// equivalent elements in the same order, using the given predicate as the
/// equivalence test.
///
/// The predicate must form an *equivalence relation* over the elements. That
/// is, for any elements `a`, `b`, and `c`, the following conditions must
/// hold:
///
/// - `areEquivalent(a, a)` is always `true`. (Reflexivity)
/// - `areEquivalent(a, b)` implies `areEquivalent(b, a)`. (Symmetry)
/// - If `areEquivalent(a, b)` and `areEquivalent(b, c)` are both `true`, then
/// `areEquivalent(a, c)` is also `true`. (Transitivity)
///
/// - Parameters:
/// - other: A BorrowingSequence to compare to this BorrowingSequence.
/// - areEquivalent: A predicate that returns `true` if its two arguments
/// are equivalent; otherwise, `false`.
/// - Returns: `true` if this BorrowingSequence and `other` contain equivalent items,
/// using `areEquivalent` as the equivalence test; otherwise, `false.`
///
/// - Complexity: O(*m*), where *m* is the count of the longer of the input sequences.
@inlinable
package func _elementsEqual<
E: Error,
Other: BorrowingSequence & ~Copyable & ~Escapable
>(
_ other: borrowing Other,
by areEquivalent: (borrowing Element, borrowing Other.Element) throws(E) -> Bool
) throws(E) -> Bool {
let it1 = self.makeBorrowingIterator()
let it2 = other.makeBorrowingIterator()
return try it1.elementsEqual(it2, by: areEquivalent)
}
}

@available(SwiftStdlib 5.0, *)
extension BorrowingIteratorProtocol
where
Self: ~Copyable & ~Escapable,
Element: Equatable
{
@inlinable
package consuming func elementsEqual<
Other: BorrowingIteratorProtocol<Element> & ~Copyable & ~Escapable
>(
_ other: consuming Other,
) -> Bool {
var result = true
_spanwiseZip(state: &result, with: other) { state, a, b in
if a.isEmpty || b.isEmpty {
state = false
return false
}
precondition(a.count == b.count)
for i in 0 ..< a.count {
guard a[unchecked: i] == b[unchecked: i] else {
state = false
return false
}
}
return true
}
return result
}

@inlinable
package consuming func _directElementsEqual<
Other: BorrowingIteratorProtocol<Element> & ~Copyable & ~Escapable
>(
_ other: consuming Other,
) -> Bool {
#if true // FIXME: rdar://150228920 Exclusive access scopes aren't expanded enough
// Note: This is the less efficient implementation of elementsEqual. The
// variant in the #else branch would be preferable, but it doesn't work yet.
// (It lets the two iterators run at their native speeds, with no artificial
// maximumCounts.)
while true {
let a = self.nextSpan()
var i = 0
if a.isEmpty {
return other.nextSpan().isEmpty
}
while i < a.count {
let b = other.nextSpan(maximumCount: a.count - i)
if b.isEmpty {
return false
}
precondition(b.count <= a.count - i)

var j = 0
while j < b.count {
guard a[unchecked: i] == b[unchecked: j] else { return false }
i &+= 1
j &+= 1
}
}
}
#else
var a = Span<Element>()
var b = Span<Element>()
loop:
while true {
if a.isEmpty {
a = self.nextSpan()
}
if b.isEmpty {
b = other.nextSpan()
}
if a.isEmpty || b.isEmpty {
return a.isEmpty && b.isEmpty
}

let c = Swift.min(a.count, b.count)
var i = 0
while i < c {
guard a[unchecked: i] == b[unchecked: i] else { return false }
i &+= 1
}
a = a.extracting(droppingFirst: c)
b = b.extracting(droppingFirst: c)
}
#endif
}
}

@available(SwiftStdlib 5.0, *)
extension BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable {
@inlinable
package consuming func elementsEqual<
E: Error,
Other: BorrowingIteratorProtocol & ~Copyable & ~Escapable
>(
_ other: consuming Other,
by areEquivalent: (borrowing Element, borrowing Other.Element) throws(E) -> Bool
) throws(E) -> Bool {
var result = true
try _spanwiseZip(state: &result, with: other) { state, a, b throws(E) in
assert(a.count == b.count || a.isEmpty || b.isEmpty)
if a.isEmpty || b.isEmpty {
state = false
return false
}
for i in 0 ..< a.count {
guard try areEquivalent(a[i], b[i]) else {
state = false
return false
}
}
return true
}
return result
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Collections open source project
//
// Copyright (c) 2026 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
//
// SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
//
//===----------------------------------------------------------------------===//

#if compiler(>=6.2) && COLLECTIONS_UNSTABLE_CONTAINERS_PREVIEW

@available(SwiftStdlib 5.0, *)
extension BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable {
@inlinable
@_lifetime(copy self)
public consuming func map<E: Error, T: ~Copyable>(
_ transform: @escaping (borrowing Element) throws(E) -> T
) throws(E) -> BorrowingMapProducer<Self, T, E> {
BorrowingMapProducer(_base: self, transform: transform)
}
}

@available(SwiftStdlib 5.0, *)
public struct BorrowingMapProducer<
Base: BorrowingIteratorProtocol & ~Copyable & ~Escapable,
Element: ~Copyable,
Error: Swift.Error
>: ~Copyable, ~Escapable {
@_alwaysEmitIntoClient
public let _transform: (borrowing Base.Element) throws(Error) -> Element

@_alwaysEmitIntoClient
public var _it: Base

@inlinable
@_lifetime(copy _base)
internal init(
_base: consuming Base,
transform: @escaping (borrowing Base.Element) throws(Error) -> Element
) {
self._transform = transform
self._it = _base
}
}

// FIXME: Sendable

@available(SwiftStdlib 5.0, *)
extension BorrowingMapProducer: Producer {
public typealias ProducerError = Error

@inlinable
public var underestimatedCount: Int {
0 // FIXME
}

@inlinable
public mutating func next() throws(ProducerError) -> Element? {
let span = _it.nextSpan(maximumCount: 1)
guard !span.isEmpty else { return nil }
return try _transform(span[unchecked: 0])
}

@inlinable
@discardableResult
@_lifetime(target: copy target)
@_lifetime(self: copy self)
public mutating func generate(
into target: inout OutputSpan<Element>
) throws(Error) -> Bool {
var success = false
while !target.isFull {
let span = _it.nextSpan(maximumCount: target.freeCapacity)
guard !span.isEmpty else { break }
success = true
var i = 0
while i < span.count {
try target.append(_transform(span[unchecked: i]))
i &+= 1
}
}
return success
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Collections open source project
//
// Copyright (c) 2026 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
//
// SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
//
//===----------------------------------------------------------------------===//

#if compiler(>=6.2) && COLLECTIONS_UNSTABLE_CONTAINERS_PREVIEW

@available(SwiftStdlib 5.0, *)
extension BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable {
@inlinable
public consuming func reduce<Result: ~Copyable, E: Error>(
_ initialResult: consuming Result,
_ nextPartialResult: (consuming Result, borrowing Element) throws(E) -> Result
) throws(E) -> Result {
var result = initialResult
while true {
let span = self.nextSpan()
guard !span.isEmpty else { break }
var i = 0
while i < span.count {
result = try nextPartialResult(result, span[unchecked: i])
i &+= 1
}
}
return result
}

@inlinable
public consuming func reduce<Result: ~Copyable, E: Error>(
into initialResult: consuming Result,
_ updateAccumulatingResult: (inout Result, borrowing Element) throws(E) -> Void
) throws(E) -> Result {
var result = initialResult
while true {
let span = self.nextSpan()
guard !span.isEmpty else { break }
var i = 0
while i < span.count {
try updateAccumulatingResult(&result, span[unchecked: i])
i &+= 1
}
}
return result
}
}

#endif
Loading
Loading