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
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.impl.ZipHandler
import com.intellij.util.containers.FactoryMap
import com.intellij.util.text.ByteArrayCharSequence
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.io.RandomAccessFile
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?
internal val file = File(path)
private val ourEntryMap: Map<String, ZipEntryDescription>
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() }
for (info in entriesMap.values) {
val file = getOrCreateFile(info, entries)
val parent = file.parent
if (parent != null) {
childrenMap[parent]?.add(file)
}
myRoot = FastJarVirtualFile(this, "", -1, DEFAULT_TIMESTAMP, null)
val filesByRelativePath = mutableMapOf<String, FastJarVirtualFile>()
filesByRelativePath[""] = myRoot
for (info in ourEntryMap.values) {
getOrCreateFile(info, filesByRelativePath)
}
val rootInfo = getEntryInfo("")
myRoot = rootInfo?.let { getOrCreateFile(it, entries) }
for (child in filesByRelativePath.values) {
child.parent?.let(childrenMap::get)?.add(child)
}
for ((key, childList) in childrenMap) {
key.children = childList.toTypedArray()
}
}
private fun getOrCreateFile(info: EntryInfo, entries: MutableMap<EntryInfo, FastJarVirtualFile>): FastJarVirtualFile {
var file = entries[info]
if (file == null) {
val parent = info.parent
file = FastJarVirtualFile(this, info.shortName,
if (info.isDirectory) -1 else info.length,
info.timestamp,
parent?.let { getOrCreateFile(it, entries) })
entries[info] = file
private fun getOrCreateFile(entry: ZipEntryDescription, map: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
val entryName = entry.relativePath.normalizePath()
return map.getOrPut(entryName) {
val (parentName, shortName) = entryName.splitPath()
val parentInfo = getOrCreateDirectory(parentName, map)
if ("." == shortName) {
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? {
return myRoot?.findFileByRelativePath(pathInJar)
}
@Throws(IOException::class)
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 {
fun contentsToByteArray(relativePath: String): ByteArray {
if (relativePath == MANIFEST_PATH) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath")
val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath")
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 DEFAULT_TIMESTAMP = 0L