@@ -12,61 +12,81 @@ import Foundation
1212
1313public extension RandomAccessCollection {
1414
15- /// Safely access ranges in this collection.
16- /// If the range you pass extends outside this collection, `nil` is returned.
17- ///
18- /// - Complexity: O(1)
15+ /// Determines whether this collection contains elements throughout the given range
1916 ///
20- /// - Parameter range: A range of valid lower and upper indices of characters in the string.
21- /// - Returns: The characters at the given index-range in this string, or `nil` if the given range is not contained
22- /// within `0` (inclusive) and `count` (exclusive).
23- @inlinable
24- subscript( orNil range: ClosedRange < Index > ) -> SubSequence ? {
25- return contains ( index: range. lowerBound) && contains ( index: range. upperBound)
26- ? self [ range]
27- : nil
17+ /// - Parameter range: The range of elements to test
18+ /// - Returns: `true` iff this collection contains elements at all the indices covered by the `range`
19+ func completelyContains< RangeType> ( range: RangeType ) -> Bool
20+ where RangeType: SafeRangeExpression ,
21+ RangeType. Bound == Index
22+ {
23+ guard let range = range. relative ( clampingIfContainedWithin: self ) else {
24+ return false
25+ }
26+
27+ return completelyContains ( range: range)
2828 }
2929
3030
31- /// Safely access ranges in this collection.
32- /// If the range you pass extends outside this collection, `nil` is returned.
31+ /// Determines whether this collection contains elements throughout the given range
3332 ///
34- /// - Complexity: O(1)
33+ /// - Parameter range: The range of elements to test
34+ /// - Returns: `true` iff this collection contains elements at all the indices covered by the `range`
35+ func completelyContains( range: Range < Index > ) -> Bool {
36+ contains ( index: range. lowerBound)
37+ && contains ( index: index ( before: range. upperBound) )
38+ }
39+
40+
41+ /// Determines whether this collection contains any elements at the given range, even if the range extends beyond this collection
3542 ///
36- /// - Parameter range: A range of valid lower and upper indices of characters in the string.
37- /// - Returns: The characters at the given index-range in this string, or `nil` if the given range is not contained
38- /// within `0` (inclusive) and `count` (inclusive).
39- @inlinable
40- subscript( orNil range: Range < Index > ) -> SubSequence ? {
41- guard !range. isEmpty else {
42- return self [ range]
43+ /// - Parameter range: The range of elements to test
44+ /// - Returns: `true` iff this collection contains elements at any of the indices covered by the `range`
45+ func rangeOverlapsThisCollection< RangeType> ( _ range: RangeType ) -> Bool
46+ where RangeType: SafeRangeExpression ,
47+ RangeType. Bound == Index
48+ {
49+ guard let range = range. relative ( clampingIfContainedWithin: self ) else {
50+ return false
4351 }
4452
45- return contains ( index: range. lowerBound) && contains ( index: index ( before: range. upperBound) )
46- ? self [ range]
47- : nil
53+ return rangeOverlapsThisCollection ( range)
4854 }
4955
5056
51- /// Safely access ranges in this collection.
52- /// If the range you pass extends outside this collection, `nil` is returned.
57+ /// Determines whether this collection contains any elements at the given range, even if the range extends beyond this collection
5358 ///
54- /// - Complexity: O(1)
59+ /// - Parameter range: The range of elements to test
60+ /// - Returns: `true` iff this collection contains elements at any of the indices covered by the `range`
61+ func rangeOverlapsThisCollection( _ range: Range < Index > ) -> Bool {
62+ contains ( index: range. lowerBound)
63+ || contains ( index: index ( before: range. upperBound) )
64+ }
65+
66+
67+ /// Some sugar for clamping the given range within this collection. If the given range doesn't overlap this collection at all, `nil` is returned.
5568 ///
56- /// - Parameter range: A range of a valid lower index of characters in the string.
57- /// - Returns: The characters at the given index-range in this string, or `nil` if the given range is not contained
58- /// within `0` (inclusive) and `count` (exclusive).
59- @inlinable
60- subscript( orNil range: PartialRangeFrom < Index > ) -> SubSequence ? {
61-
62- guard range. lowerBound != endIndex else {
63- // `self[self.endIndex...]` is always valid, resulting in an empty subsequence
64- return self [ range]
65- }
66-
67- return contains ( index: range. lowerBound)
68- ? self [ range. lowerBound... ]
69- : nil
69+ /// This sugar sweetens `SafeRangeExpression`'s `relative(clampingTo:)` method.
70+ ///
71+ /// - Parameter range: The range to clamp to this collection
72+ /// - Returns: A range which is definitely within this collecion, or `nil` if it doesn't overlap this collection
73+ @inline ( __always)
74+ func clamp< RangeType> ( range: RangeType ) -> Range < Index > ?
75+ where RangeType: SafeRangeExpression ,
76+ RangeType. Bound == Index
77+ {
78+ range. relative ( clampingIfContainedWithin: self )
79+ }
80+
81+
82+ /// A generic implementation of the below `[orNil:]` subscripts
83+ @inline ( __always)
84+ @usableFromInline
85+ internal subscript< RangeType> ( _orNil_getOnly range: RangeType ) -> SubSequence ?
86+ where RangeType: SafeRangeExpression ,
87+ RangeType. Bound == Index
88+ {
89+ clamp ( range: range) . map { self [ $0] }
7090 }
7191
7292
@@ -75,35 +95,44 @@ public extension RandomAccessCollection {
7595 ///
7696 /// - Complexity: O(1)
7797 ///
78- /// - Parameter range: A range of a valid upper index of characters in the string .
79- /// - Returns: The characters at the given index-range in this string, or `nil` if the given range's bound is not
80- /// within `0` (inclusive) and `count` (inclusive ).
98+ /// - Parameter range: A range of valid indices of characters in the collection .
99+ /// - Returns: The characters at the given index-range in this string, or `nil` if the given range is not contained
100+ /// within `0` (inclusive) and `count` (exclusive ).
81101 @inlinable
82- subscript( orNil range: PartialRangeUpTo < Index > ) -> SubSequence ? {
83-
84- guard range. upperBound != startIndex else {
85- // `self[..<self.startIndex]` is always valid, resulting in an empty subsequence
86- return self [ range]
87- }
88-
89- return contains ( index: index ( before: range. upperBound) )
90- ? self [ orNil: startIndex ..< range. upperBound]
91- : nil
102+ subscript< RangeType> ( orNil range: RangeType ) -> SubSequence ?
103+ where RangeType: SafeRangeExpression ,
104+ RangeType. Bound == Index {
105+ self [ _orNil_getOnly: range]
92106 }
93107
94108
95- /// Safely access ranges in this collection.
96- /// If the range you pass extends outside this collection, `nil` is returned.
109+ /// Safely access or mutate ranges in this collection.
110+ /// If the range you pass extends outside this collection, `nil` is returned, or if you're trying to mutate this collection, nothing is done.
111+ /// Setting the value to `nil` is interpreted as removing the range from this collection
97112 ///
98113 /// - Complexity: O(1)
99114 ///
100- /// - Parameter range: A range of a valid upper index of characters in the string .
101- /// - Returns: The characters at the given index-range in this string, or `nil` if the given range's bound is not
115+ /// - Parameter range: A range of valid indices of characters in the collection .
116+ /// - Returns: The characters at the given index-range in this string, or `nil` if the given range is not contained
102117 /// within `0` (inclusive) and `count` (exclusive).
103118 @inlinable
104- subscript( orNil range: PartialRangeThrough < Index > ) -> SubSequence ? {
105- return contains ( index: range. upperBound)
106- ? self [ orNil: startIndex ... range. upperBound]
107- : nil
119+ subscript< RangeType> ( orNil range: RangeType ) -> SubSequence ?
120+ where RangeType: SafeRangeExpression ,
121+ RangeType. Bound == Index ,
122+ Self: MutableCollection ,
123+ Self: RangeReplaceableCollection
124+ {
125+ get { self [ _orNil_getOnly: range] }
126+
127+ set {
128+ guard rangeOverlapsThisCollection ( range) else { return }
129+
130+ if let newValue = newValue {
131+ self [ range] = newValue
132+ }
133+ else {
134+ self . removeSubrange ( range)
135+ }
136+ }
108137 }
109138}
0 commit comments