Rework FastJarHandler

1. Do not inherit from ZipHandler
2. Previously, the following computations have been happening:
- Map<String, ZipEntryDescription> -> Map<String, EntryInfo> (see createEntriesMap)
- Map<String, EntryInfo> -> VirtualFile tree

But the intermediate computations (Map<String, EntryInfo>)
were only used in the constructor, thus they've eliminated in this commit

3. Unclear magic semantic from `getOrCreate` with "/" and "\\" (copy-pasted from CoreJarHandler)
has been replaced with `normalizePath`
This commit is contained in:
Denis.Zharkov
2021-07-29 11:04:15 +03:00
committed by teamcityserver
parent edf9f5e647
commit 979f5e8443
@@ -4,18 +4,17 @@
*/ */
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.impl.ZipHandler
import com.intellij.util.containers.FactoryMap import com.intellij.util.containers.FactoryMap
import com.intellij.util.text.ByteArrayCharSequence import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.IOException
import java.io.RandomAccessFile import java.io.RandomAccessFile
import java.nio.channels.FileChannel import java.nio.channels.FileChannel
class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandler(path) { class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) {
private val myRoot: VirtualFile? private val myRoot: VirtualFile?
internal val file = File(path)
private val ourEntryMap: Map<String, ZipEntryDescription> private val ourEntryMap: Map<String, ZipEntryDescription>
private val cachedManifest: ByteArray? private val cachedManifest: ByteArray?
@@ -33,112 +32,80 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandl
} }
} }
val entries: MutableMap<EntryInfo, FastJarVirtualFile> = HashMap()
val entriesMap = entriesMap
val childrenMap = FactoryMap.create<FastJarVirtualFile, MutableList<VirtualFile>> { ArrayList() } val childrenMap = FactoryMap.create<FastJarVirtualFile, MutableList<VirtualFile>> { ArrayList() }
for (info in entriesMap.values) {
val file = getOrCreateFile(info, entries) myRoot = FastJarVirtualFile(this, "", -1, DEFAULT_TIMESTAMP, null)
val parent = file.parent
if (parent != null) { val filesByRelativePath = mutableMapOf<String, FastJarVirtualFile>()
childrenMap[parent]?.add(file) filesByRelativePath[""] = myRoot
}
for (info in ourEntryMap.values) {
getOrCreateFile(info, filesByRelativePath)
} }
val rootInfo = getEntryInfo("") for (child in filesByRelativePath.values) {
myRoot = rootInfo?.let { getOrCreateFile(it, entries) } child.parent?.let(childrenMap::get)?.add(child)
}
for ((key, childList) in childrenMap) { for ((key, childList) in childrenMap) {
key.children = childList.toTypedArray() key.children = childList.toTypedArray()
} }
} }
private fun getOrCreateFile(info: EntryInfo, entries: MutableMap<EntryInfo, FastJarVirtualFile>): FastJarVirtualFile { private fun getOrCreateFile(entry: ZipEntryDescription, map: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
var file = entries[info] val entryName = entry.relativePath.normalizePath()
if (file == null) {
val parent = info.parent return map.getOrPut(entryName) {
file = FastJarVirtualFile(this, info.shortName, val (parentName, shortName) = entryName.splitPath()
if (info.isDirectory) -1 else info.length,
info.timestamp, val parentInfo = getOrCreateDirectory(parentName, map)
parent?.let { getOrCreateFile(it, entries) }) if ("." == shortName) {
entries[info] = file return parentInfo
}
FastJarVirtualFile(
this, shortName,
if (entry.isDirectory) -1 else entry.uncompressedSize.toLong(),
DEFAULT_TIMESTAMP,
parentInfo
)
} }
return file }
private fun getOrCreateDirectory(entryName: String, map: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
return map.getOrPut(entryName) {
val entry = ourEntryMap["$entryName/"]
if (entry != null) {
return getOrCreateFile(entry, map)
}
val (parentPath, shortName) = entryName.splitPath()
require(entryName != parentPath) {
"invalid entry name: '" + entryName + "' in " + this.file.absolutePath + "; after split: " + Pair(parentPath, shortName)
}
val parentInfo = getOrCreateDirectory(parentPath, map)
FastJarVirtualFile(this, shortName, -1, DEFAULT_TIMESTAMP, parentInfo)
}
}
private fun String.normalizePath(): String {
if (endsWith('/')) return substring(0, length - 1).normalizePath()
if (startsWith('/') || startsWith('\\')) return substring(1, length).normalizePath()
if (!contains('\\')) return this
return FileUtil.normalize(this)
}
private fun String.splitPath(): Pair<String, String> {
val slashIndex = lastIndexOf('/')
if (slashIndex == -1) return Pair("", this)
return Pair(substring(0, slashIndex), substring(slashIndex + 1))
} }
fun findFileByPath(pathInJar: String): VirtualFile? { fun findFileByPath(pathInJar: String): VirtualFile? {
return myRoot?.findFileByRelativePath(pathInJar) return myRoot?.findFileByRelativePath(pathInJar)
} }
@Throws(IOException::class) fun contentsToByteArray(relativePath: String): ByteArray {
override fun createEntriesMap(): Map<String, EntryInfo> {
val mapToEntryInfo = mutableMapOf<String, EntryInfo>()
mapToEntryInfo[""] = EntryInfo("", true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, null)
for (zipEntry in ourEntryMap.values) {
getOrCreate(zipEntry, mapToEntryInfo)
}
return mapToEntryInfo
}
private fun getOrCreate(entry: ZipEntryDescription, map: MutableMap<String, EntryInfo>): EntryInfo {
var isDirectory = entry.isDirectory
var entryName = entry.relativePath
if (StringUtil.endsWithChar(entryName, '/')) {
entryName = entryName.substring(0, entryName.length - 1)
isDirectory = true
}
if (StringUtil.startsWithChar(entryName, '/') || StringUtil.startsWithChar(entryName, '\\')) {
entryName = entryName.substring(1)
}
var info = map[entryName]
if (info != null) return info
val path = splitPathAndFix(entryName)
val parentInfo = getOrCreateDirectory(path.first, map)
if ("." == path.second) {
return parentInfo
}
info = store(map, parentInfo, path.second, isDirectory, entry.uncompressedSize.toLong(), 0, path.third)
return info
}
private fun getOrCreateDirectory(entryName: String, map: MutableMap<String, EntryInfo>): EntryInfo {
var info = map[entryName]
if (info == null) {
val entry = ourEntryMap["$entryName/"]
if (entry != null) {
return getOrCreate(entry, map)
}
val path = splitPathAndFix(entryName)
require(entryName != path.first) {
"invalid entry name: '" + entryName + "' in " + this.file.absolutePath + "; after split: " + path
}
val parentInfo = getOrCreateDirectory(path.first, map)
info = store(map, parentInfo, path.second, true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, path.third)
}
return info
}
private fun store(
map: MutableMap<String, EntryInfo>,
parentInfo: EntryInfo?,
shortName: CharSequence,
isDirectory: Boolean,
size: Long,
time: Long,
entryName: String
): EntryInfo {
val sequence = ByteArrayCharSequence.convertToBytesIfPossible(shortName)
val info = EntryInfo(sequence, isDirectory, size, time, parentInfo)
map[entryName] = info
return info
}
override fun contentsToByteArray(relativePath: String): ByteArray {
if (relativePath == MANIFEST_PATH) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath") if (relativePath == MANIFEST_PATH) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath")
val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath") val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath")
return fileSystem.cachedOpenFileHandles[file].use { return fileSystem.cachedOpenFileHandles[file].use {
@@ -151,4 +118,4 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandl
private const val MANIFEST_PATH = "META-INF/MANIFEST.MF" private const val MANIFEST_PATH = "META-INF/MANIFEST.MF"
private const val DEFAULT_TIMESTAMP = 0L