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:
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
|
||||||
|
|||||||
Reference in New Issue
Block a user