Skip to content

Commit 92e704a

Browse files
committed
stability: bound rendered string search payloads
1 parent 74c77c1 commit 92e704a

2 files changed

Lines changed: 79 additions & 3 deletions

File tree

app/src/main/java/com/kyhsgeekcode/disassembler/ui/tabs/StringTab.kt

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@ import kotlinx.coroutines.flow.StateFlow
2727
import timber.log.Timber
2828

2929
private const val MAX_RENDERED_STRING_RESULTS = 5_000
30+
private const val MAX_RENDERED_STRING_CHARS = 4_096
31+
private const val MAX_RENDERED_STRING_TOTAL_CHARS = 32_768
32+
33+
internal fun clipFoundStringForRendering(
34+
result: FoundString,
35+
maxChars: Int = MAX_RENDERED_STRING_CHARS
36+
): Pair<FoundString, Boolean> {
37+
require(maxChars >= 0) { "maxChars must be non-negative" }
38+
if (result.string.length <= maxChars) {
39+
return result to false
40+
}
41+
if (maxChars == 0) {
42+
return result.copy(string = "") to true
43+
}
44+
val clippedString = if (maxChars <= 3) {
45+
result.string.take(maxChars)
46+
} else {
47+
result.string.take(maxChars - 3) + "..."
48+
}
49+
return result.copy(string = clippedString) to true
50+
}
3051

3152
class StringSearchResultAccumulator(private val maxResults: Int) {
3253
private val _results = mutableListOf<FoundString>()
@@ -36,12 +57,27 @@ class StringSearchResultAccumulator(private val maxResults: Int) {
3657
var isTruncated: Boolean = false
3758
private set
3859

60+
private var renderedChars: Int = 0
61+
3962
fun append(result: FoundString) {
4063
if (_results.size >= maxResults) {
4164
isTruncated = true
4265
return
4366
}
44-
_results.add(result)
67+
val remainingChars = MAX_RENDERED_STRING_TOTAL_CHARS - renderedChars
68+
if (remainingChars <= 0) {
69+
isTruncated = true
70+
return
71+
}
72+
val (displayResult, wasClipped) = clipFoundStringForRendering(
73+
result,
74+
minOf(MAX_RENDERED_STRING_CHARS, remainingChars)
75+
)
76+
if (wasClipped) {
77+
isTruncated = true
78+
}
79+
_results.add(displayResult)
80+
renderedChars += displayResult.string.length
4581
}
4682
}
4783

@@ -61,8 +97,8 @@ class StringTabData(val data: TabKind.FoundString) : PreparedTabData() {
6197
analyzer.searchStrings(data.range.first, data.range.last) { p, t, fs ->
6298
fs?.let {
6399
accumulator.append(it)
64-
if (!accumulator.isTruncated) {
65-
strings.add(it)
100+
if (strings.size < accumulator.results.size) {
101+
strings.add(accumulator.results.last())
66102
}
67103
}
68104
if (p == t) { // done

app/src/test/java/com/kyhsgeekcode/disassembler/ui/tabs/StringSearchResultAccumulatorTest.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,44 @@ class StringSearchResultAccumulatorTest {
3535
assertEquals(2, accumulator.results.size)
3636
assertTrue(accumulator.isTruncated)
3737
}
38+
39+
@Test
40+
fun `accumulator clips oversized string payloads for rendering`() {
41+
val accumulator = StringSearchResultAccumulator(maxResults = 2)
42+
43+
accumulator.append(
44+
FoundString(
45+
offset = 1,
46+
length = 5_000,
47+
string = "A".repeat(5_000)
48+
)
49+
)
50+
51+
assertTrue(accumulator.isTruncated)
52+
assertTrue(
53+
accumulator.results.single().string.length <= 4_096,
54+
"Large string payloads should be clipped before they reach the UI list"
55+
)
56+
}
57+
58+
@Test
59+
fun `accumulator bounds total rendered string payload`() {
60+
val accumulator = StringSearchResultAccumulator(maxResults = 10)
61+
62+
repeat(10) {
63+
accumulator.append(
64+
FoundString(
65+
offset = it.toLong(),
66+
length = 5_000,
67+
string = "A".repeat(5_000)
68+
)
69+
)
70+
}
71+
72+
assertTrue(accumulator.isTruncated)
73+
assertTrue(
74+
accumulator.results.sumOf { it.string.length } <= 32_768,
75+
"Large result sets should stop growing once the UI payload budget is exhausted"
76+
)
77+
}
3878
}

0 commit comments

Comments
 (0)