Skip to content

Commit 2558c9c

Browse files
Optimize build-time memory usage in string processing (#25)
Refactored StringRegistryImpl and DeobfuscatorGenerator to use a streaming approach for handling obfuscated string chunks.
1 parent 2f2777f commit 2558c9c

File tree

2 files changed

+55
-10
lines changed

2 files changed

+55
-10
lines changed

processor/src/main/kotlin/org/lsposed/lsparanoid/processor/DeobfuscatorGenerator.kt

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,32 @@ class DeobfuscatorGenerator(
7070

7171
private fun ClassVisitor.generateStaticInitializer() {
7272
newMethod(Opcodes.ACC_STATIC, METHOD_STATIC_INITIALIZER) {
73-
val chunks = stringRegistry.getAllChunks()
74-
push(chunks.size)
73+
val chunkCount = stringRegistry.getChunkCount()
74+
push(chunkCount)
7575
newArray(CHUNKS_ELEMENT_TYPE)
76+
// Store the new array into the static field
7677
putStatic(deobfuscator.type.toAsmType(), CHUNKS_FIELD_NAME, CHUNKS_FIELD_TYPE)
7778

78-
getStatic(deobfuscator.type.toAsmType(), CHUNKS_FIELD_NAME, CHUNKS_FIELD_TYPE)
79-
chunks.forEachIndexed { index, chunk ->
80-
dup()
81-
push(index)
82-
push(chunk)
83-
arrayStore(CHUNKS_ELEMENT_TYPE)
79+
// If there are chunks to add, load the field back onto the stack
80+
if (chunkCount > 0) {
81+
getStatic(deobfuscator.type.toAsmType(), CHUNKS_FIELD_NAME, CHUNKS_FIELD_TYPE)
82+
var index = 0
83+
stringRegistry.streamChunks { chunk ->
84+
dup() // Duplicate the array reference
85+
push(index) // Push the index
86+
push(chunk) // Push the chunk string
87+
arrayStore(CHUNKS_ELEMENT_TYPE) // Store the chunk in the array
88+
index++
89+
}
90+
// Pop the array reference that was loaded by getStatic (if chunkCount > 0)
91+
// and duplicated in the loop. The original putStatic already stored it.
92+
// If streamChunks did nothing (e.g. 0 chunks), this pop is not needed as getStatic wasn't called.
93+
// However, the structure with index ensures it's balanced if chunks were processed.
94+
// If chunkCount > 0, one array reference remains on stack after loop.
95+
pop()
8496
}
85-
pop()
97+
// Ensure stack is balanced: if chunkCount was 0, no putStatic/getStatic/pop happened here.
98+
// If chunkCount > 0, array was putStatic, then getStatic, then loop, then pop. Correct.
8699
}
87100
}
88101

processor/src/main/kotlin/org/lsposed/lsparanoid/processor/StringRegistry.kt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ import org.lsposed.lsparanoid.RandomHelper
2222

2323
interface StringRegistry {
2424
fun registerString(string: String): Long
25+
26+
@Deprecated("Use streamChunks for better memory efficiency", ReplaceWith("streamChunks(consumer)"))
2527
fun getAllChunks(): List<String>
28+
29+
fun streamChunks(consumer: (String) -> Unit)
30+
fun getChunkCount(): Int
2631
}
2732

2833
class StringRegistryImpl(
@@ -53,7 +58,34 @@ class StringRegistryImpl(
5358
return id
5459
}
5560

61+
@Deprecated("Use streamChunks for better memory efficiency", ReplaceWith("streamChunks(consumer)"))
5662
override fun getAllChunks(): List<String> {
57-
return builder.toString().chunked(DeobfuscatorHelper.MAX_CHUNK_LENGTH)
63+
// This implementation remains for compatibility but should not be used for large datasets.
64+
val chunks = mutableListOf<String>()
65+
streamChunks { chunks.add(it) }
66+
return chunks
67+
}
68+
69+
override fun streamChunks(consumer: (String) -> Unit) {
70+
val totalLength = builder.length
71+
if (totalLength == 0) {
72+
return
73+
}
74+
var currentIndex = 0
75+
while (currentIndex < totalLength) {
76+
val endIndex = kotlin.math.min(currentIndex + DeobfuscatorHelper.MAX_CHUNK_LENGTH, totalLength)
77+
// Substring still creates a new string, but it's one chunk at a time
78+
// instead of builder.toString() creating one giant string first.
79+
consumer(builder.substring(currentIndex, endIndex))
80+
currentIndex = endIndex
81+
}
82+
}
83+
84+
override fun getChunkCount(): Int {
85+
val totalLength = builder.length
86+
if (totalLength == 0) {
87+
return 0
88+
}
89+
return (totalLength + DeobfuscatorHelper.MAX_CHUNK_LENGTH - 1) / DeobfuscatorHelper.MAX_CHUNK_LENGTH
5890
}
5991
}

0 commit comments

Comments
 (0)