-
Notifications
You must be signed in to change notification settings - Fork 440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix and improve Chain
's index offsetting logic
#22
Conversation
Chain
's index offsetting logicChain
's index offsetting logic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, @timvermeulen — thank you! 👏
let d = base1.distance(from: i, to: base1.endIndex) | ||
if n < d { | ||
if limit >= i { | ||
// `limit` is relevant, so `base2` cannot be reached |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good observation!
return base1.index(i, offsetBy: n, limitedBy: limit) | ||
.map(Index.init(first:)) | ||
} else if let j = base1.index(i, offsetBy: n, limitedBy: base1.endIndex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish there was an operation that combined this calculation and finding d
in the next branch, but no luck, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree 100% to the extent that I added the equivalent for Rust's Iterator
recently, specifically to speed up Chain
🙂 This would make for a good optimization point if we ever get the chance.
The next best thing would be to write a regular function for this which somehow detects whether the collection supports random-access, is there any hope of that being possible to implement? I assume that info does exist at runtime but I haven't found a way to extract it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree 100% to the extent that I added the equivalent for Rust's
Iterator
recently, specifically to speed upChain
🙂 This would make for a good optimization point if we ever get the chance.
Exactly. I think the only place we have anything like that is in the innards of capturing a sequence in an array, where we return the buffer along with in-progress iterator.
The next best thing would be to write a regular function for this which somehow detects whether the collection supports random-access, is there any hope of that being possible to implement? I assume that info does exist at runtime but I haven't found a way to extract it.
There is a way, but since even non-random-access collections can provide a faster index(_:offsetBy:)
, I don't think we'd want to go that route…
Chain
's index offsetting logic is untested and contains a few bugs, all these assertions fail:I've put the fixes for these in a separate commit for reference.
Another issue with the current implementation is that
chain.index(i, offsetBy: n)
(withi
pointing intobase1
) always computes the distance fromi
tobase1.endIndex
, regardless ofn
. As a result, this code traverses the entire string:This PR fixes this by first calling
base1.index(i, offsetBy: n, limitedBy: base1.endIndex)
before potentially computing the distance tobase1.endIndex
.This does still mean that when crossing the boundary between
base1
andbase2
, the suffix ofbase1
is traversed twice. If we could determine whether or notBase1: RandomAccessCollection
then we could conditionally replace this logic by repeatedbase1.index(after:)
calls, but I'm not sure if that's possible...Checklist