Skip to content

Commit

Permalink
Use firsIndexAssumingSorted when searching in sorted arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulTaykalo committed Oct 26, 2019
1 parent a9f79ab commit 73d26bf
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
internal extension RandomAccessCollection where Index == Int {
/// Returns the first index in which an element of the collection satisfies
/// the given predicate. The collection assumed to be sorted. If collection is not have sorted values
/// the result is undefined
///
/// The idea is to get first index of a function where predicate starting to return true values.
///
/// let values = [1,2,3,4,5]
/// let idx = values.firstIndexAssumingSorted(where: { $0 > 3 })
///
/// // false, false, false, true, true
/// // ^
/// // therefore idx == 3
///
/// - Parameter predicate: A closure that takes an element as its argument
/// and returns a Boolean value that indicates whether the passed element
/// represents a match.
/// - Returns: The index of the first element for which `predicate` returns
/// `true`. If no elements in the collection satisfy the given predicate,
/// returns `nil`.
///
/// - Complexity: O(log(*n*)), where *n* is the length of the collection.
@inlinable
func firstIndexAssumingSorted(where predicate: (Self.Element) throws -> Bool) rethrows -> Int? {
// Predicate should divide a collection to two pars of vaues
// "bad" values for which predicate returns `false``
// "good" values for which predicate return `true`

// false false false false false true true true
// ^
// The idea is to get _first_ index which for which predicate returns `true`

let lastIndex = count

// The index that represetns where bad values starts.
var badIndex = -1

// The index that represetns where good values starts
var goodIndex = lastIndex
var midIndex = (badIndex + goodIndex) / 2

while badIndex + 1 < goodIndex {
if try predicate(self[midIndex]) {
goodIndex = midIndex
} else {
badIndex = midIndex
}
midIndex = (badIndex + goodIndex) / 2
}

// Corner case, we' re out of bound, no good items in array
if midIndex == lastIndex {
return nil
}
return goodIndex
}
}
46 changes: 5 additions & 41 deletions Source/SwiftLintFramework/Extensions/SyntaxMap+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,23 @@ extension SyntaxMap {
.intersects(byteRange)
}

guard let startIndex = firstIntersectingTokenIndex(inByteRange: byteRange) else {
return []
func intersectsOrAfter(_ token: SyntaxToken) -> Bool {
return token.offset + token.length > byteRange.location
}

func isAfterByteRange(_ token: SyntaxToken) -> Bool {
return token.offset < byteRange.upperBound
guard let startIndex = tokens.firstIndexAssumingSorted(where: intersectsOrAfter) else {
return []
}

let tokensAfterFirstIntersection = tokens
.lazy
.suffix(from: startIndex)
.prefix(while: isAfterByteRange)
.prefix(while: { $0.offset < byteRange.upperBound })
.filter(intersect)

return Array(tokensAfterFirstIntersection)
}

// Index of first token which intersects byterange
// Using binary search
func firstIntersectingTokenIndex(inByteRange byteRange: NSRange) -> Int? {
let lastIndex = tokens.count

// The idx, which definitely beyound byteOffset
var bad = -1

// The index which is definitely after byteOffset
var good = lastIndex
var mid = (bad + good) / 2

// 0 0 0 0 0 1 1 1 1 1
// ^
// The idea is to get _first_ token index which intesects the byteRange
func intersectsOrAfter(at index: Int) -> Bool {
let token = tokens[index]
return token.offset + token.length > byteRange.location
}

while bad + 1 < good {
if intersectsOrAfter(at: mid) {
good = mid
} else {
bad = mid
}
mid = (bad + good) / 2
}

// Corner case, we' re out of bound, no good items in array
if mid == lastIndex {
return nil
}
return good
}

internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] {
return tokens(inByteRange: byteRange).kinds
}
Expand Down

0 comments on commit 73d26bf

Please sign in to comment.