Use MappedBuffer for FastJarFileSystem implementation

This commit is contained in:
Denis.Zharkov
2021-03-24 15:15:38 +03:00
committed by TeamCityServer
parent 8f06e59d3b
commit 33cf058b55
2 changed files with 45 additions and 140 deletions
@@ -14,6 +14,8 @@ import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.io.RandomAccessFile
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import java.util.*
internal class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandler(path) {
@@ -24,9 +26,9 @@ internal class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) :
init {
RandomAccessFile(file, "r").use { randomAccessFile ->
val bufferedRandomAccessFile = BufferedRandomAccessFile(randomAccessFile)
ourEntryMap = bufferedRandomAccessFile.parseCentralDirectory().associateBy { it.relativePath }
cachedManifest = ourEntryMap[MANIFEST_PATH]?.let(bufferedRandomAccessFile::contentsToByteArray)
val mappedByteBuffer = randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length())
ourEntryMap = mappedByteBuffer.parseCentralDirectory().associateBy { it.relativePath }
cachedManifest = ourEntryMap[MANIFEST_PATH]?.let(mappedByteBuffer::contentsToByteArray)
}
val entries: MutableMap<EntryInfo, FastJarVirtualFile> = HashMap()
@@ -139,7 +141,7 @@ internal class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) :
val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath")
return cachedOpenFileHandles[file].use {
synchronized(it) {
it.get().contentsToByteArray(zipEntryDescription)
it.get().second.contentsToByteArray(zipEntryDescription)
}
}
}
@@ -147,16 +149,19 @@ internal class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) :
private const val MANIFEST_PATH = "META-INF/MANIFEST.MF"
private val cachedOpenFileHandles: FileAccessorCache<File, BufferedRandomAccessFile> =
object : FileAccessorCache<File, BufferedRandomAccessFile>(20, 10) {
typealias RandomAccessFileAndBuffer = Pair<RandomAccessFile, MappedByteBuffer>
private val cachedOpenFileHandles: FileAccessorCache<File, RandomAccessFileAndBuffer> =
object : FileAccessorCache<File, RandomAccessFileAndBuffer>(20, 10) {
@Throws(IOException::class)
override fun createAccessor(file: File): BufferedRandomAccessFile {
return BufferedRandomAccessFile(RandomAccessFile(file, "r"))
override fun createAccessor(file: File): RandomAccessFileAndBuffer {
val randomAccessFile = RandomAccessFile(file, "r")
return Pair(randomAccessFile, randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()))
}
@Throws(IOException::class)
override fun disposeAccessor(fileAccessor: BufferedRandomAccessFile) {
fileAccessor.close()
override fun disposeAccessor(fileAccessor: RandomAccessFileAndBuffer) {
fileAccessor.first.close()
}
override fun isEqual(val1: File, val2: File): Boolean {
@@ -5,8 +5,7 @@
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
import java.io.File
import java.io.RandomAccessFile
import java.nio.MappedByteBuffer
import java.util.zip.Inflater
@@ -30,24 +29,17 @@ private const val END_OF_CENTRAL_DIR_SIZE = 22
private const val LOCAL_FILE_HEADER_EXTRA_OFFSET = 28
private const val LOCAL_FILE_HEADER_SIZE = LOCAL_FILE_HEADER_EXTRA_OFFSET + 2
fun File.contentsToByteArray(zipEntryDescription: ZipEntryDescription): ByteArray {
return RandomAccessFile(this, "r").use { randomAccessFile ->
val bufferedRandomAccessFile = BufferedRandomAccessFile(randomAccessFile)
bufferedRandomAccessFile.contentsToByteArray(zipEntryDescription)
}
}
fun BufferedRandomAccessFile.contentsToByteArray(
fun MappedByteBuffer.contentsToByteArray(
zipEntryDescription: ZipEntryDescription
): ByteArray {
val extraSize =
readShortLittleEndianFromOffset((zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_EXTRA_OFFSET).toLong())
readShortLittleEndianFromOffset((zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_EXTRA_OFFSET))
seek(
(zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_SIZE + zipEntryDescription.fileNameSize + extraSize).toLong()
position(
zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_SIZE + zipEntryDescription.fileNameSize + extraSize
)
val compressed = ByteArray(zipEntryDescription.compressedSize + 1)
readFully(compressed, zipEntryDescription.compressedSize)
get(compressed, 0, zipEntryDescription.compressedSize)
return when (zipEntryDescription.compressionKind) {
ZipEntryDescription.CompressionKind.DEFLATE -> {
@@ -64,22 +56,15 @@ fun BufferedRandomAccessFile.contentsToByteArray(
}
}
fun File.parseCentralDirectory(): List<ZipEntryDescription> {
return RandomAccessFile(this, "r").use { randomAccessFile ->
BufferedRandomAccessFile(randomAccessFile).parseCentralDirectory()
}
}
fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
fun BufferedRandomAccessFile.parseCentralDirectory(): List<ZipEntryDescription> {
val randomAccessFile = randomAccessFile
val endOfCentralDirectoryOffset = (randomAccessFile.length() - END_OF_CENTRAL_DIR_SIZE downTo 0).first { offset ->
val endOfCentralDirectoryOffset = (capacity() - END_OF_CENTRAL_DIR_SIZE downTo 0).first { offset ->
// header of "End of central directory"
randomAccessFile.readIntLittleEndianFromOffset(offset) == 0x06054b50
readIntLittleEndianFromOffset(offset) == 0x06054b50
}
val entriesNumber = randomAccessFile.readShortLittleEndianFromOffset(endOfCentralDirectoryOffset + 10)
val offsetOfCentralDirectory = randomAccessFile.readIntLittleEndianFromOffset(endOfCentralDirectoryOffset + 16).toLong()
val entriesNumber = readShortLittleEndianFromOffset(endOfCentralDirectoryOffset + 10)
val offsetOfCentralDirectory = readIntLittleEndianFromOffset(endOfCentralDirectoryOffset + 16)
var currentOffset = offsetOfCentralDirectory
@@ -103,7 +88,11 @@ fun BufferedRandomAccessFile.parseCentralDirectory(): List<ZipEntryDescription>
val offsetOfFileData = readIntLittleEndianFromOffset(currentOffset + 42)
val name = readString(fileNameLength)
val bytesForName = ByteArray(fileNameLength)
get(bytesForName)
val name = String(bytesForName)
currentOffset += 46 + fileNameLength + extraLength + fileCommentLength
@@ -126,117 +115,28 @@ fun BufferedRandomAccessFile.parseCentralDirectory(): List<ZipEntryDescription>
return result
}
class BufferedRandomAccessFile(
val randomAccessFile: RandomAccessFile
) {
private val buffer = ByteArray(BUFFER_SIZE)
private var offsetInBuffer: Int = -1
private var currentBegin: Long = -1L
fun seek(globalOffset: Long, force: Boolean = false) {
if (!force && currentBegin != -1L && globalOffset >= currentBegin && globalOffset < currentBegin + BUFFER_SIZE) {
offsetInBuffer = (globalOffset - currentBegin).toInt()
return
}
offsetInBuffer = 0
currentBegin = globalOffset
randomAccessFile.seek(globalOffset)
randomAccessFile.readFully(buffer, 0, minOf(randomAccessFile.length() - globalOffset, BUFFER_SIZE.toLong()).toInt())
}
fun read(): Int {
require(offsetInBuffer < BUFFER_SIZE)
return buffer[offsetInBuffer++].let {
if (offsetInBuffer == BUFFER_SIZE && currentBegin + BUFFER_SIZE < randomAccessFile.length()) {
seek(currentBegin + BUFFER_SIZE)
}
java.lang.Byte.toUnsignedInt(it)
}
}
fun readString(length: Int): String {
if (length > BUFFER_SIZE) {
randomAccessFile.seek(currentBegin + offsetInBuffer)
val byteArray = ByteArray(length)
randomAccessFile.readFully(byteArray)
return String(byteArray)
}
if (length > BUFFER_SIZE - offsetInBuffer) {
seek(currentBegin + offsetInBuffer, force = true)
}
return String(buffer, offsetInBuffer, length)
}
fun readFully(dst: ByteArray, length: Int) {
if (length > BUFFER_SIZE) {
randomAccessFile.seek(currentBegin + offsetInBuffer)
randomAccessFile.readFully(dst, 0, length)
return
}
if (length > BUFFER_SIZE - offsetInBuffer) {
seek(currentBegin + offsetInBuffer, force = true)
}
System.arraycopy(buffer, offsetInBuffer, dst, 0, length)
}
fun close() {
randomAccessFile.close()
}
companion object {
private const val BUFFER_SIZE = 50000
}
}
private fun RandomAccessFile.readIntLittleEndianFromOffset(offset: Long): Int {
seek(offset)
private fun MappedByteBuffer.readIntLittleEndianFromOffset(offset: Int): Int {
position(offset)
return readIntLittleEndian()
}
private fun RandomAccessFile.readIntLittleEndian(): Int {
val a = this.read()
val b = this.read()
val c = this.read()
val d = this.read()
private fun MappedByteBuffer.getByteAsInt(): Int = java.lang.Byte.toUnsignedInt(get())
private fun MappedByteBuffer.readIntLittleEndian(): Int {
val a = this.getByteAsInt()
val b = this.getByteAsInt()
val c = this.getByteAsInt()
val d = this.getByteAsInt()
return (d shl 24) + (c shl 16) + (b shl 8) + a
}
private fun RandomAccessFile.readShortLittleEndianFromOffset(offset: Long): Int {
seek(offset)
private fun MappedByteBuffer.readShortLittleEndianFromOffset(offset: Int): Int {
position(offset)
return readShortLittleEndian()
}
private fun RandomAccessFile.readShortLittleEndian(): Int {
val a = this.read()
val b = this.read()
return (b shl 8) + a
}
private fun BufferedRandomAccessFile.readIntLittleEndianFromOffset(offset: Long): Int {
seek(offset)
return readIntLittleEndian()
}
private fun BufferedRandomAccessFile.readIntLittleEndian(): Int {
val a = this.read()
val b = this.read()
val c = this.read()
val d = this.read()
return (d shl 24) + (c shl 16) + (b shl 8) + a
}
private fun BufferedRandomAccessFile.readShortLittleEndianFromOffset(offset: Long): Int {
seek(offset)
return readShortLittleEndian()
}
private fun BufferedRandomAccessFile.readShortLittleEndian(): Int {
val a = this.read()
val b = this.read()
private fun MappedByteBuffer.readShortLittleEndian(): Int {
val a = this.getByteAsInt()
val b = this.getByteAsInt()
return (b shl 8) + a
}