@@ -60,13 +60,11 @@ public struct CompactedCollection<Base: Collection, Element>: Collection
60
60
@usableFromInline
61
61
let base : Base
62
62
63
- @usableFromInline
64
- let _startIdx : Base . Index
65
-
66
63
@inlinable
67
64
init ( base: Base ) {
68
65
self . base = base
69
- self . _startIdx = base. firstIndex ( where: { $0 != nil } ) ?? base. endIndex
66
+ let idx = base. firstIndex ( where: { $0 != nil } ) ?? base. endIndex
67
+ self . startIndex = Index ( base: idx)
70
68
}
71
69
72
70
public struct Index {
@@ -79,9 +77,7 @@ public struct CompactedCollection<Base: Collection, Element>: Collection
79
77
}
80
78
}
81
79
82
- /// Complexity: O(1)
83
- @inlinable
84
- public var startIndex : Index { Index ( base: _startIdx) }
80
+ public var startIndex : Index
85
81
86
82
@inlinable
87
83
public var endIndex : Index { Index ( base: base. endIndex) }
@@ -93,19 +89,13 @@ public struct CompactedCollection<Base: Collection, Element>: Collection
93
89
94
90
@inlinable
95
91
public func index( after i: Index ) -> Index {
96
- guard i. base != base . endIndex else {
97
- return Index ( base : base . endIndex )
92
+ guard i != endIndex else {
93
+ fatalError ( " Index out of bounds " )
98
94
}
99
-
100
- var idx = base. index ( after: i. base)
101
- while idx != base. endIndex {
102
- guard base [ idx] != nil else {
103
- base. formIndex ( after: & idx)
104
- continue
105
- }
106
- return Index ( base: idx)
107
- }
108
- return Index ( base: base. endIndex)
95
+ let baseIdx = base. index ( after: i. base)
96
+ guard let idx = base [ baseIdx... ] . firstIndex ( where: { $0 != nil } )
97
+ else { return endIndex }
98
+ return Index ( base: idx)
109
99
}
110
100
}
111
101
@@ -114,19 +104,14 @@ extension CompactedCollection: BidirectionalCollection
114
104
115
105
@inlinable
116
106
public func index( before i: Index ) -> Index {
117
- guard i. base != base. startIndex else {
118
- return Index ( base: base. startIndex)
119
- }
120
-
121
- var idx = base. index ( before: i. base)
122
- while idx > base. startIndex {
123
- guard base [ idx] != nil else {
124
- base. formIndex ( before: & idx)
125
- continue
126
- }
127
- return Index ( base: idx)
107
+ guard i != startIndex else {
108
+ fatalError ( " Index out of bounds " )
128
109
}
129
- return Index ( base: base. startIndex)
110
+
111
+ let baseIdx = base. index ( before: i. base)
112
+ guard let idx = base [ ... baseIdx] . lastIndex ( where: { $0 != nil } )
113
+ else { return Index ( base: base. startIndex) }
114
+ return Index ( base: idx)
130
115
}
131
116
}
132
117
@@ -138,6 +123,9 @@ extension CompactedCollection.Index: Comparable {
138
123
}
139
124
}
140
125
126
+ extension CompactedCollection . Index : Hashable
127
+ where Base. Index: Hashable { }
128
+
141
129
extension Collection {
142
130
/// Complexity: O(*n*) where *n* is the number of elements in the
143
131
/// base collection.
@@ -150,25 +138,58 @@ extension Collection {
150
138
151
139
extension CompactedSequence : LazySequenceProtocol
152
140
where Base: LazySequenceProtocol { }
153
- extension CompactedSequence : Equatable where Base: Equatable { }
154
- extension CompactedSequence : Hashable where Base: Hashable { }
155
141
156
142
extension CompactedCollection : RandomAccessCollection
157
143
where Base: RandomAccessCollection { }
158
144
extension CompactedCollection : LazySequenceProtocol
159
145
where Base: LazySequenceProtocol { }
160
146
extension CompactedCollection : LazyCollectionProtocol
161
147
where Base: LazyCollectionProtocol { }
162
- extension CompactedCollection : Equatable where Base: Equatable { }
163
-
164
- // Since we have another stored property of type `Base.Index` on the
165
- // collection, synthesis of `Hashble` conformace would require
166
- // a `Base.Index: Hashable` constraint, so we implement the hasher
167
- // only in terms of `base`. Since the computed index is based on it,
168
- // it should not make a difference here.
169
- extension CompactedCollection : Hashable where Base: Hashable {
148
+
149
+
150
+ // Hashable and Equatable conformance is based on each non-nil
151
+ // element on base collection.
152
+ extension CompactedSequence : Equatable
153
+ where Base: Equatable , Base. Element: Equatable {
154
+
155
+ @inlinable
156
+ public static func == ( lhs: CompactedSequence ,
157
+ rhs: CompactedSequence ) -> Bool {
158
+ lhs. compactMap ( { $0 } )
159
+ . elementsEqual ( rhs. compactMap ( { $0 } ) )
160
+ }
161
+ }
162
+
163
+ extension CompactedSequence : Hashable
164
+ where Base: Hashable , Base. Element: Hashable {
165
+ @inlinable
166
+ public func hash( into hasher: inout Hasher ) {
167
+ for element in base {
168
+ guard let unwrapped = element else { continue }
169
+ hasher. combine ( unwrapped)
170
+ }
171
+ }
172
+ }
173
+
174
+ extension CompactedCollection : Equatable
175
+ where Base: Equatable , Base. Element: Equatable {
176
+
177
+ @inlinable
178
+ public static func == ( lhs: CompactedCollection ,
179
+ rhs: CompactedCollection ) -> Bool {
180
+ lhs. compactMap ( { $0 } )
181
+ . elementsEqual ( rhs. compactMap ( { $0 } ) )
182
+ }
183
+ }
184
+
185
+ extension CompactedCollection : Hashable
186
+ where Base: Hashable , Base. Element: Hashable {
187
+
170
188
@inlinable
171
189
public func hash( into hasher: inout Hasher ) {
172
- hasher. combine ( base)
190
+ for element in base {
191
+ guard let unwrapped = element else { continue }
192
+ hasher. combine ( unwrapped)
193
+ }
173
194
}
174
195
}
0 commit comments