@@ -254,98 +254,123 @@ extension Collection {
254
254
/// A collection that presents the elements of its base collection
255
255
/// in `SubSequence` chunks of any given size.
256
256
///
257
- /// A ChunkedCollection is a lazy view on the base Collection, but it does not implicitly confer
257
+ /// A `ChunkedByCount` is a lazy view on the base Collection, but it does not implicitly confer
258
258
/// laziness on algorithms applied to its result. In other words, for ordinary collections `c`:
259
259
///
260
260
/// * `c.chunks(of: 3)` does not create new storage
261
261
/// * `c.chunks(of: 3).map(f)` maps eagerly and returns a new array
262
262
/// * `c.lazy.chunks(of: 3).map(f)` maps lazily and returns a `LazyMapCollection`
263
- public struct ChunkedCollection < Base: Collection > {
263
+ public struct ChunkedByCount < Base: Collection > {
264
264
265
265
public typealias Element = Base . SubSequence
266
266
267
- public let base : Base
268
267
@usableFromInline
269
- internal let _chunkCount : Int
268
+ internal let base : Base
269
+
270
+ @usableFromInline
271
+ internal let chunkCount : Int
270
272
273
+ @usableFromInline
274
+ internal var computedStartIndex : Index
275
+
271
276
/// Creates a view instance that presents the elements of `base`
272
277
/// in `SubSequence` chunks of the given size.
273
278
///
274
- /// - Complexity: O(1 )
279
+ /// - Complexity: O(n )
275
280
@inlinable
276
281
internal init ( _base: Base , _chunkCount: Int ) {
277
282
self . base = _base
278
- self . _chunkCount = _chunkCount
283
+ self . chunkCount = _chunkCount
284
+
285
+ // Compute the start index upfront in order to make
286
+ // start index a O(1) lookup.
287
+ let baseEnd = _base. index (
288
+ _base. startIndex, offsetBy: _chunkCount,
289
+ limitedBy: _base. endIndex
290
+ ) ?? _base. endIndex
291
+
292
+ self . computedStartIndex =
293
+ Index ( _baseRange: _base. startIndex..< baseEnd)
279
294
}
280
295
}
281
296
282
- extension ChunkedCollection : Collection {
297
+ extension ChunkedByCount : Collection {
283
298
public struct Index {
284
- public let base : Base . Index
299
+ @usableFromInline
300
+ internal let baseRange : Range < Base . Index >
285
301
286
302
@usableFromInline
287
- init ( _base : Base . Index ) {
288
- self . base = _base
303
+ internal init ( _baseRange : Range < Base . Index > ) {
304
+ self . baseRange = _baseRange
289
305
}
290
306
}
291
-
292
- public var startIndex : Index { Index ( _base: base. startIndex) }
293
- public var endIndex : Index { Index ( _base: base. endIndex) }
307
+
308
+ public var startIndex : Index { computedStartIndex }
309
+ public var endIndex : Index {
310
+ Index ( _baseRange: base. endIndex..< base. endIndex)
311
+ }
294
312
295
313
public subscript( i: Index ) -> Element {
296
- base [ i. base ..< index ( after : i ) . base ]
314
+ base [ i. baseRange ]
297
315
}
298
316
299
317
@inlinable
300
318
public func index( after i: Index ) -> Index {
301
319
let baseIdx = base. index (
302
- i. base, offsetBy: _chunkCount, limitedBy: base. endIndex)
303
- return Index ( _base: baseIdx ?? base. endIndex)
320
+ i. baseRange. upperBound, offsetBy: chunkCount,
321
+ limitedBy: base. endIndex
322
+ ) ?? base. endIndex
323
+ return Index ( _baseRange: i. baseRange. upperBound..< baseIdx)
304
324
}
305
325
}
306
326
307
- extension ChunkedCollection . Index : Comparable {
327
+ extension ChunkedByCount . Index : Comparable {
308
328
@inlinable
309
- public static func < ( lhs: ChunkedCollection . Index ,
310
- rhs: ChunkedCollection . Index ) -> Bool {
311
- lhs. base < rhs. base
329
+ public static func < ( lhs: ChunkedByCount . Index ,
330
+ rhs: ChunkedByCount . Index ) -> Bool {
331
+ lhs. baseRange . lowerBound < rhs. baseRange . lowerBound
312
332
}
313
333
}
314
334
315
- extension ChunkedCollection :
335
+ extension ChunkedByCount :
316
336
BidirectionalCollection , RandomAccessCollection
317
337
where Base: RandomAccessCollection {
318
338
@inlinable
319
339
public func index( before i: Index ) -> Index {
320
- if i. base == base. endIndex {
321
- let remainder = base. count% _chunkCount
340
+ var offset = chunkCount
341
+ if i. baseRange. lowerBound == base. endIndex {
342
+ let remainder = base. count% chunkCount
322
343
if remainder != 0 {
323
- return Index ( _base : base . index ( i . base , offsetBy : - remainder) )
344
+ offset = remainder
324
345
}
325
346
}
326
- return Index ( _base: base. index ( i. base, offsetBy: - _chunkCount) )
347
+
348
+ let baseIdx = base. index (
349
+ i. baseRange. lowerBound, offsetBy: - offset,
350
+ limitedBy: base. startIndex
351
+ ) ?? base. startIndex
352
+ return Index ( _baseRange: baseIdx..< i. baseRange. lowerBound)
327
353
}
328
354
329
355
@inlinable
330
356
public func distance( from start: Index , to end: Index ) -> Int {
331
- let distance = base. distance ( from: start. base, to: end. base)
357
+ let distance =
358
+ base. distance ( from: start. baseRange. lowerBound,
359
+ to: end. baseRange. lowerBound)
332
360
let ( quotient, remainder) =
333
- distance. quotientAndRemainder ( dividingBy: _chunkCount )
361
+ distance. quotientAndRemainder ( dividingBy: chunkCount )
334
362
// Increment should account for negative distances.
335
363
if remainder < 0 {
336
364
return quotient - 1
337
365
}
338
366
return quotient + ( remainder == 0 ? 0 : 1 )
339
367
}
340
-
368
+
341
369
@inlinable
342
- public func index( _ i: Index , offsetBy n: Int ) -> Index {
343
- guard n != 0 else { return i }
344
-
345
- let bound = n > 0 ? base. endIndex : base. startIndex
346
- return Index ( _base:
347
- base. index ( i. base, offsetBy: n * _chunkCount, limitedBy: bound) ?? bound
348
- )
370
+ public var count : Int {
371
+ let ( quotient, remainder) =
372
+ base. count. quotientAndRemainder ( dividingBy: chunkCount)
373
+ return quotient + ( remainder == 0 ? 0 : 1 )
349
374
}
350
375
}
351
376
@@ -367,12 +392,17 @@ extension Collection {
367
392
///
368
393
/// - Complexity: O(1)
369
394
@inlinable
370
- public func chunks( ofCount count: Int ) -> ChunkedCollection < Self > {
395
+ public func chunks( ofCount count: Int ) -> ChunkedByCount < Self > {
371
396
precondition ( count > 0 , " Cannot chunk with count <= 0! " )
372
- return ChunkedCollection ( _base: self , _chunkCount: count)
397
+ return ChunkedByCount ( _base: self , _chunkCount: count)
373
398
}
374
399
}
375
400
376
401
// Conditional conformances.
377
- extension ChunkedCollection : Equatable where Base: Equatable { }
378
- extension ChunkedCollection : Hashable where Base: Hashable { }
402
+ extension ChunkedByCount : Equatable where Base: Equatable { }
403
+ extension ChunkedByCount : Hashable where Base: Hashable , Base. Index: Hashable { }
404
+ extension ChunkedByCount . Index : Hashable where Base. Index: Hashable { }
405
+
406
+ // Lazy conditional conformance.
407
+ extension ChunkedByCount : LazySequenceProtocol where Base: LazySequenceProtocol { }
408
+ extension ChunkedByCount : LazyCollectionProtocol where Base: LazyCollectionProtocol { }
0 commit comments