Gradle, ProcessedFilesCache: optimize serialization
JSON kept for debugability
This commit is contained in:
+103
-22
@@ -6,7 +6,9 @@
|
||||
package org.jetbrains.kotlin.gradle.internal
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonParseException
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.internal.hash.FileHasher
|
||||
@@ -15,8 +17,8 @@ import java.io.File
|
||||
|
||||
/**
|
||||
* Cache for preventing processing some files twice.
|
||||
* Files are precessed some subdirectories of [targetDir] called `target`.
|
||||
* For each target [getOrCompute] should be called.
|
||||
* Files are precessed to some subdirectories of [targetDir] called `target`.
|
||||
* [getOrCompute] returns path to directory with files produced for given input file.
|
||||
* Absent target directories will be deleted on [close].
|
||||
* `compute` will be called only for source files that was changed since last processing.
|
||||
*
|
||||
@@ -33,16 +35,104 @@ internal open class ProcessedFilesCache(
|
||||
private val hasher = (project as ProjectInternal).services.get(FileHasher::class.java)
|
||||
private val gson = GsonBuilder().setPrettyPrinting().create()
|
||||
|
||||
private fun readFrom(json: JsonReader): State? {
|
||||
val result = State()
|
||||
|
||||
json.obj {
|
||||
check(json.nextName() == "version")
|
||||
val version = json.nextString()
|
||||
if (version != this.version) return null
|
||||
|
||||
check(json.nextName() == "items")
|
||||
json.obj {
|
||||
while (json.peek() == JsonToken.NAME) {
|
||||
val key = json.nextName()
|
||||
json.beginObject()
|
||||
check(json.nextName() == "src")
|
||||
val src = json.nextString()
|
||||
|
||||
var target: String? = null
|
||||
if (json.peek() == JsonToken.NAME) {
|
||||
check(json.nextName() == "target")
|
||||
if (json.peek() != JsonToken.NULL) {
|
||||
target = json.nextString()
|
||||
}
|
||||
}
|
||||
json.endObject()
|
||||
|
||||
result[decodeHexString(key)] = Element(src, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun State.writeTo(json: JsonWriter) {
|
||||
json.obj {
|
||||
json.name("version").value(version)
|
||||
json.name("items")
|
||||
json.obj {
|
||||
byHash.forEach {
|
||||
json.name(it.key.toHex())
|
||||
json.obj {
|
||||
json.name("src").value(it.value.src)
|
||||
json.name("target")
|
||||
if (it.value.target == null) json.nullValue() else json.value(it.value.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun JsonReader.obj(body: () -> Unit) {
|
||||
beginObject()
|
||||
body()
|
||||
endObject()
|
||||
}
|
||||
|
||||
private inline fun JsonWriter.obj(body: () -> Unit) {
|
||||
beginObject()
|
||||
body()
|
||||
endObject()
|
||||
}
|
||||
|
||||
fun ByteArray.toHex(): String {
|
||||
val result = CharArray(size * 2) { ' ' }
|
||||
var i = 0
|
||||
forEach {
|
||||
val n = it.toInt()
|
||||
result[i++] = Character.forDigit(n shr 4 and 0xF, 16)
|
||||
result[i++] = Character.forDigit(n and 0xF, 16)
|
||||
}
|
||||
return String(result)
|
||||
}
|
||||
|
||||
private fun decodeHexString(hexString: String): ByteArray {
|
||||
check(hexString.length % 2 == 0)
|
||||
val bytes = ByteArray(hexString.length / 2)
|
||||
var i = 0
|
||||
var o = 0
|
||||
while (i < hexString.length) {
|
||||
bytes[o++] = hexToByte(hexString[i++], hexString[i++])
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
private fun hexToByte(a: Char, b: Char): Byte = ((a.toDigit() shl 4) + b.toDigit()).toByte()
|
||||
|
||||
private fun Char.toDigit(): Int = Character.digit(this, 16).also { check(it != -1) }
|
||||
|
||||
private class State {
|
||||
var version: String? = null
|
||||
val byHash = mutableMapOf<String, Element>()
|
||||
val byHash = mutableMapOf<ByteArray, Element>()
|
||||
val byTarget = mutableMapOf<String, Element>()
|
||||
|
||||
operator fun get(elementHash: ByteArray) =
|
||||
byHash[elementHash.toHexString()]
|
||||
operator fun get(elementHash: ByteArray) = byHash[elementHash]
|
||||
|
||||
operator fun set(elementHash: ByteArray, element: Element) {
|
||||
byHash[elementHash.toHexString()] = element
|
||||
elementHash.toHexString()
|
||||
byHash[elementHash] = element
|
||||
val target = element.target
|
||||
if (target != null) {
|
||||
byTarget[target] = element
|
||||
@@ -62,15 +152,10 @@ internal open class ProcessedFilesCache(
|
||||
targetDir.mkdirs()
|
||||
|
||||
val state: State? = if (stateFile.exists()) try {
|
||||
stateFile.reader().use {
|
||||
gson.fromJson(it, State::class.java)
|
||||
}.let {
|
||||
if (it.version != version) {
|
||||
targetDir.deleteRecursively()
|
||||
null
|
||||
} else it
|
||||
gson.newJsonReader(stateFile.reader()).use {
|
||||
readFrom(it)
|
||||
}
|
||||
} catch (e: JsonParseException) {
|
||||
} catch (e: Throwable) {
|
||||
project.logger.warn("Cannot read $stateFile", e)
|
||||
if (targetDir.exists()) {
|
||||
targetDir.deleteRecursively()
|
||||
@@ -83,10 +168,6 @@ internal open class ProcessedFilesCache(
|
||||
|
||||
private val new = State()
|
||||
|
||||
init {
|
||||
new.version = version
|
||||
}
|
||||
|
||||
internal fun getOrCompute(
|
||||
file: File,
|
||||
compute: () -> String?
|
||||
@@ -144,8 +225,8 @@ internal open class ProcessedFilesCache(
|
||||
}
|
||||
|
||||
stateFile.parentFile.mkdirs()
|
||||
stateFile.writer().use {
|
||||
gson.toJson(new, it)
|
||||
gson.newJsonWriter(stateFile.writer()).use {
|
||||
new.writeTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ internal class GradleNodeModulesCache(val nodeJs: NodeJsRootExtension) : AutoClo
|
||||
|
||||
val project: Project get() = nodeJs.project
|
||||
internal val dir = nodeJs.root.nodeModulesGradleCacheDir
|
||||
private val cache = ProcessedFilesCache(project, dir, STATE_FILE_NAME, "8")
|
||||
private val cache = ProcessedFilesCache(project, dir, STATE_FILE_NAME, "9")
|
||||
|
||||
@Synchronized
|
||||
fun get(
|
||||
|
||||
Reference in New Issue
Block a user