Skip to content

Commit

Permalink
Simplify hashtag line checker to avoid infinite loop in android match…
Browse files Browse the repository at this point in the history
…er (#4779)

https://masto.nyc/@GetMisch/113557332197065306 causes an infinite loop
in the native matcher with the original pattern, trying a different
approach
  • Loading branch information
Tak authored Dec 4, 2024
1 parent 96ed6e1 commit af5d10c
Show file tree
Hide file tree
Showing 2 changed files with 3 additions and 6 deletions.
7 changes: 3 additions & 4 deletions app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,8 @@ fun setClickableText(
}
}

private val trailingHashtagExpression by unsafeLazy {
Pattern.compile("""$WORD_BREAK_EXPRESSION(#$HASHTAG_EXPRESSION$WORD_BREAK_FROM_SPACE_EXPRESSION+)*""", Pattern.CASE_INSENSITIVE)
}
private val hashtagWithHashPattern = Pattern.compile("^#$HASHTAG_EXPRESSION$")
private val whitespacePattern = Regex("""\s+""")

/**
* Find the "trailing" hashtags in spanned content
Expand All @@ -131,7 +130,7 @@ private val trailingHashtagExpression by unsafeLazy {
internal fun getTrailingHashtags(content: Spanned): Pair<Int, List<HashTag>> {
// split() instead of lines() because we need to be able to account for the length of the removed delimiter
val trailingContentLength = content.split('\r', '\n').asReversed().takeWhile { line ->
line.isBlank() || trailingHashtagExpression.matcher(line).matches()
line.splitToSequence(whitespacePattern).all { it.isBlank() || hashtagWithHashPattern.matcher(it).matches() }
}.sumOf { it.length + 1 } // length + 1 to include the stripped line ending character

return when (trailingContentLength) {
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/java/com/keylesspalace/tusky/util/StringUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import kotlin.random.Random

private const val POSSIBLE_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

const val WORD_BREAK_EXPRESSION = """(^|$|[^\p{L}\p{N}_])"""
const val WORD_BREAK_FROM_SPACE_EXPRESSION = """(^|$|\s)"""
const val HASHTAG_EXPRESSION = "([\\w_]*[\\p{Alpha}_][\\w_]*)"
val hashtagPattern = Pattern.compile(HASHTAG_EXPRESSION, Pattern.CASE_INSENSITIVE or Pattern.MULTILINE)

Expand Down

0 comments on commit af5d10c

Please sign in to comment.