New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add RangeSet and discontiguous collection operations #28161
Conversation
This adds the RangeSet and DiscontiguousSlice types, as well as collection operations for working with discontiguous ranges of elements. This also adds a COWLoggingArray type to the test suite to verify that mutable collection algorithms don't perform unexpected copy-on-write operations when mutating slices mid-operation.
@swift-ci Please smoke test |
@swift-ci Please smoke test |
9481251
to
5321c34
Compare
@swift-ci Please smoke test |
Just use a `Range<T>` as is for the single-range case. If there's a benefit to making range sets more compact we can move toward that instead.
@swift-ci Please smoke test |
stdlib/public/core/RangeSet.swift
Outdated
/// A set of ranges of any comparable value. | ||
/// A set of any comparable value, represented by ranges. | ||
/// | ||
/// You can use a range set to efficiently represent a set `Comparable` values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
“set of”
stdlib/public/core/RangeSet.swift
Outdated
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
/// A set of any comparable value, represented by ranges. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
“any comparable value__s__”?
@swift-ci Please smoke test |
This method was removed from the final approved SE proposal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Talked with Nate on the phone, recording some points here.
stdlib/public/core/RangeSet.swift
Outdated
/// - `_indicesOfRange(12..<19) == 1..<2` | ||
/// - `_indicesOfRange(17..<19) == 2..<2` | ||
/// - `_indicesOfRange(12..<22) == 1..<3` | ||
func _indicesOfRange(_ range: Range<Bound>) -> Range<Int> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicit internal
/// - Complexity: O(*n* log *n*) where *n* is the number of elements. | ||
/// - Precondition: | ||
/// `n == distance(from: range.lowerBound, to: range.upperBound)` | ||
@usableFromInline |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably drop
/// | ||
/// - Complexity: O(log *n*), where *n* is the length of this collection if | ||
/// the collection conforms to `RandomAccessCollection`, otherwise O(*n*). | ||
@usableFromInline |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably drop
/// - Complexity: O(*n* log *n*) where *n* is the length of the collection. | ||
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) | ||
@discardableResult | ||
@inlinable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop inlinable
, doesn't buy us anything as written. If you add the rotate
fast path, then hand-outline the rest of this code so it can be inlined. The end result is the same at run-time for users, but we have less code size and branches in user code.
/// - Returns: The new index of the element that was first pre-rotation. | ||
/// | ||
/// - Complexity: O(*n*) | ||
@usableFromInline |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably drop
/// - index: The index to include in the range set. `index` must be a | ||
/// valid index of `collection` that isn't the collection's `endIndex`. | ||
/// - collection: The collection that contains `index`. | ||
public init<S, C>(_ indices: S, within collection: C) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a candidate for inlinable
, depending on need. It's a bigger code size tradeoff than insert
below.
/// | ||
/// - Complexity: O(*n*), where *n* is the number of ranges in the range | ||
/// set. | ||
public mutating func remove<C>(_ index: Bound, within collection: C) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inlinable
could be a win.
/// - Complexity: O(*n*), where *n* is the number of ranges in the range | ||
/// set. | ||
@usableFromInline | ||
internal func _inverted<C>(within collection: C) -> RangeSet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Candidate for inlinable
if this is called heavily in other inlinable code.
stdlib/public/core/RangeSet.swift
Outdated
internal struct _Pair<Element>: RandomAccessCollection { | ||
var pair: (first: Element, second: Element) | ||
|
||
init(_ first: Element, _ second: Element) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
explicit internal
on all these entities
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
struct _RangeSetStorage<T: Comparable> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
explicit internal
In particular, only adding inlining when functions can reduce or eliminate generic parameters. I'll follow up with benchmarks to test the effects of adding more inlinability.
@swift-ci Please smoke test |
where Base: BidirectionalCollection | ||
{ | ||
public func index(before i: Index) -> Index { | ||
_precondition(i != startIndex, "Can't move index before startIndex") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a policy for when we include out of bounds checks in index(after:)
/index(before:)
?
/// - distance(from: lhs.lowerBound, to: p) == distance(from: | ||
/// rhs.lowerBound, to: q) | ||
/// - p == lhs.upperBound || q == rhs.upperBound | ||
mutating func _swapNonemptySubrangePrefixes( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: missing an explicit internal
With availability annotations, the RangeSet subscript overloads aren't coming through for these tests.
@swift-ci Please smoke test |
@swift-ci Please smoke test |
@compnerd: I am far from a real computer, but it seems that the Windows failures might simply be fixed moving the |
Validation test just effectively disables it (I need to rebase my patch to enable that on the Azure agents). Could just be something we are setting for that test suite. I'm waiting for an update to complete; Ill look at it once the update is complete. |
I think that there is a missing dependency |
switch (lhs._storage, rhs._storage) { | ||
case (.empty, .empty): | ||
return true | ||
case (.empty, .singleRange), (.singleRange, .empty): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When case .singleRange is empty, doesn’t it equal to case .empty?
This adds the
RangeSet
andDiscontiguousSlice
types, as well as collection operations for working with discontiguous ranges of elements. This also adds aCOWLoggingArray
type to the test suite to verify that mutable collection algorithms don't perform unexpected copy-on-write operations when mutating slices mid-operation.Still to do:
RangeSet
set algebra methods (since we aren't conforming, we don't inherit the docs from the protocol)