@@ -392,66 +392,112 @@ extension ChunkedByCount {
392392 guard limit != i else { return nil }
393393
394394 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)
399398 } 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)
404402 }
405403 }
406404
407405 @inlinable
408406 public func index( _ i: Index , offsetBy distance: Int ) -> Index {
409407 guard distance != 0 else { return i }
410408
411- return distance > 0
409+ let idx = distance > 0
412410 ? offsetForward ( i, offsetBy: distance)
413411 : offsetBackward ( i, offsetBy: distance)
412+ guard let index = idx else {
413+ fatalError ( " Out of bounds " )
414+ }
415+ return index
414416 }
415417
416418 @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 ? {
418422 assert ( distance > 0 )
423+
419424 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: >
421428 )
422429 }
423430
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 {
428437 let remainder = base. count% chunkCount
429438 // We have to take it into account when calculating offsets.
430439 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
438444 }
439445 }
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)
440456 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: <
442460 )
443461 }
444462
445463 // Helper to compute index(offsetBy:) index.
446464 @inline ( __always)
447465 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 (
451470 i. baseRange. lowerBound, offsetBy: baseDistance,
452471 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+ }
454499
500+ let baseStartIdx = baseIdx ?? baseBound
455501 let baseEndIdx = base. index (
456502 baseStartIdx, offsetBy: chunkCount, limitedBy: base. endIndex
457503 ) ?? base. endIndex
0 commit comments