@@ -392,66 +392,112 @@ extension ChunkedByCount {
392
392
guard limit != i else { return nil }
393
393
394
394
if offset > 0 {
395
- guard limit < i || distance ( from: i, to: limit) >= offset else {
396
- return nil
397
- }
398
- return offsetForward ( i, offsetBy: offset)
395
+ return limit > i
396
+ ? offsetForward ( i, offsetBy: offset, limit: limit)
397
+ : offsetForward ( i, offsetBy: offset)
399
398
} else {
400
- guard limit > i || distance ( from: i, to: limit) <= offset else {
401
- return nil
402
- }
403
- return offsetBackward ( i, offsetBy: offset)
399
+ return limit < i
400
+ ? offsetBackward ( i, offsetBy: offset, limit: limit)
401
+ : offsetBackward ( i, offsetBy: offset)
404
402
}
405
403
}
406
404
407
405
@inlinable
408
406
public func index( _ i: Index , offsetBy distance: Int ) -> Index {
409
407
guard distance != 0 else { return i }
410
408
411
- return distance > 0
409
+ let idx = distance > 0
412
410
? offsetForward ( i, offsetBy: distance)
413
411
: offsetBackward ( i, offsetBy: distance)
412
+ guard let index = idx else {
413
+ fatalError ( " Out of bounds " )
414
+ }
415
+ return index
414
416
}
415
417
416
418
@usableFromInline
417
- internal func offsetForward( _ i: Index , offsetBy distance: Int ) -> Index {
419
+ internal func offsetForward(
420
+ _ i: Index , offsetBy distance: Int , limit: Index ? = nil
421
+ ) -> Index ? {
418
422
assert ( distance > 0 )
423
+
419
424
return makeOffsetIndex (
420
- from: i, baseBound: base. endIndex, baseDistance: distance * chunkCount
425
+ from: i, baseBound: base. endIndex,
426
+ distance: distance, baseDistance: distance * chunkCount,
427
+ limit: limit, by: >
421
428
)
422
429
}
423
430
424
- @usableFromInline
425
- internal func offsetBackward( _ i: Index , offsetBy distance: Int ) -> Index {
426
- assert ( distance < 0 )
427
- if i. baseRange. lowerBound == base. endIndex {
431
+ // Convenience to compute offset backward base distance.
432
+ @inline ( __always)
433
+ private func computeOffsetBackwardBaseDistance(
434
+ _ i: Index , _ distance: Int
435
+ ) -> Int {
436
+ if i == endIndex {
428
437
let remainder = base. count% chunkCount
429
438
// We have to take it into account when calculating offsets.
430
439
if remainder != 0 {
431
- // Distance "minus" one(at this point distance is negative) because we
432
- // need to adjust for the last position that have a variadic(remainder)
433
- // number of elements.
434
- let baseDistance = ( ( distance + 1 ) * chunkCount) - remainder
435
- return makeOffsetIndex (
436
- from: i, baseBound: base. startIndex, baseDistance: baseDistance
437
- )
440
+ // Distance "minus" one(at this point distance is negative)
441
+ // because we need to adjust for the last position that have
442
+ // a variadic(remainder) number of elements.
443
+ return ( ( distance + 1 ) * chunkCount) - remainder
438
444
}
439
445
}
446
+ return distance * chunkCount
447
+ }
448
+
449
+ @usableFromInline
450
+ internal func offsetBackward(
451
+ _ i: Index , offsetBy distance: Int , limit: Index ? = nil
452
+ ) -> Index ? {
453
+ assert ( distance < 0 )
454
+ let baseDistance =
455
+ computeOffsetBackwardBaseDistance ( i, distance)
440
456
return makeOffsetIndex (
441
- from: i, baseBound: base. startIndex, baseDistance: distance * chunkCount
457
+ from: i, baseBound: base. startIndex,
458
+ distance: distance, baseDistance: baseDistance,
459
+ limit: limit, by: <
442
460
)
443
461
}
444
462
445
463
// Helper to compute index(offsetBy:) index.
446
464
@inline ( __always)
447
465
private func makeOffsetIndex(
448
- from i: Index , baseBound: Base . Index , baseDistance: Int
449
- ) -> Index {
450
- let baseStartIdx = base. index (
466
+ from i: Index , baseBound: Base . Index , distance: Int , baseDistance: Int ,
467
+ limit: Index ? , by limitFn: ( Base . Index , Base . Index ) -> Bool
468
+ ) -> Index ? {
469
+ let baseIdx = base. index (
451
470
i. baseRange. lowerBound, offsetBy: baseDistance,
452
471
limitedBy: baseBound
453
- ) ?? baseBound
472
+ )
473
+
474
+ if let limit = limit {
475
+ if baseIdx == nil {
476
+ // If we past the bounds while advancing forward and the
477
+ // limit is the `endIndex`, since the computation on base
478
+ // don't take into account the remainder, we have to make
479
+ // sure that passing the bound was because of the distance
480
+ // not just because of a remainder. Special casing is less
481
+ // expensive than always use count(which could be O(n) for
482
+ // non-random access collection base) to compute the base
483
+ // distance taking remainder into account.
484
+ if baseDistance > 0 && limit == endIndex {
485
+ if self . distance ( from: i, to: limit) < distance {
486
+ return nil
487
+ }
488
+ } else {
489
+ return nil
490
+ }
491
+ }
492
+
493
+ // Checks for the limit.
494
+ let baseStartIdx = baseIdx ?? baseBound
495
+ if limitFn ( baseStartIdx, limit. baseRange. lowerBound) {
496
+ return nil
497
+ }
498
+ }
454
499
500
+ let baseStartIdx = baseIdx ?? baseBound
455
501
let baseEndIdx = base. index (
456
502
baseStartIdx, offsetBy: chunkCount, limitedBy: base. endIndex
457
503
) ?? base. endIndex
0 commit comments