Made files opened in read mode seekable more reliably.

This commit is contained in:
Florian Nücke 2014-04-20 22:12:00 +02:00
parent bbe80bc5de
commit d0d4f502ba
7 changed files with 69 additions and 34 deletions

View File

@ -94,7 +94,7 @@ trait Buffered extends OutputStreamFileSystem {
FileUtils.deleteQuietly(childFile)
childFile.createNewFile()
val out = new io.FileOutputStream(childFile).getChannel
val in = java.nio.channels.Channels.newChannel(openInputStream(childPath).get)
val in = openInputChannel(childPath).get
out.transferFrom(in, 0, Long.MaxValue)
out.close()
in.close()

View File

@ -25,8 +25,8 @@ class CC15FileSystem(val mount: IMount) extends InputStreamFileSystem {
// ----------------------------------------------------------------------- //
protected def openInputStream(path: String) = try {
Some(mount.openForRead(path))
protected def openInputChannel(path: String) = try {
Some(new InputFileChannel(mount.openForRead(path)))
} catch {
case _: Throwable => None
}

View File

@ -25,8 +25,8 @@ class CC16FileSystem(val mount: IMount) extends InputStreamFileSystem {
// ----------------------------------------------------------------------- //
protected def openInputStream(path: String) = try {
Some(mount.openForRead(path))
protected def openInputChannel(path: String) = try {
Some(new InputFileChannel(mount.openForRead(path)))
} catch {
case _: Throwable => None
}

View File

@ -1,6 +1,7 @@
package li.cil.oc.server.fs
import java.io
import java.nio.channels.SeekableByteChannel
trait FileInputStreamFileSystem extends InputStreamFileSystem {
protected val root: io.File
@ -43,6 +44,6 @@ trait FileInputStreamFileSystem extends InputStreamFileSystem {
// ----------------------------------------------------------------------- //
override protected def openInputStream(path: String): Option[io.InputStream] =
Some(new io.FileInputStream(new io.File(root, path)))
override protected def openInputChannel(path: String): Option[SeekableByteChannel] =
Some(new io.RandomAccessFile(new io.File(root, path), "r").getChannel)
}

View File

@ -1,10 +1,12 @@
package li.cil.oc.server.fs
import java.io.{FileNotFoundException, IOException, InputStream}
import java.io.{FileNotFoundException, IOException}
import li.cil.oc.api
import li.cil.oc.api.fs.Mode
import net.minecraft.nbt.{NBTTagList, NBTTagCompound}
import scala.collection.mutable
import java.nio.channels.SeekableByteChannel
import java.nio.ByteBuffer
trait InputStreamFileSystem extends api.fs.FileSystem {
private val handles = mutable.Map.empty[Int, Handle]
@ -25,9 +27,9 @@ trait InputStreamFileSystem extends api.fs.FileSystem {
override def open(path: String, mode: Mode) = this.synchronized(if (mode == Mode.Read && exists(path) && !isDirectory(path)) {
val handle = Iterator.continually((Math.random() * Int.MaxValue).toInt + 1).filterNot(handles.contains).next()
openInputStream(path) match {
case Some(stream) =>
handles += handle -> new Handle(this, handle, path, stream)
openInputChannel(path) match {
case Some(channel) =>
handles += handle -> new Handle(this, handle, path, channel)
handle
case _ => throw new FileNotFoundException()
}
@ -49,10 +51,10 @@ trait InputStreamFileSystem extends api.fs.FileSystem {
val handle = handleNbt.getInteger("handle")
val path = handleNbt.getString("path")
val position = handleNbt.getLong("position")
openInputStream(path) match {
case Some(stream) =>
val fileHandle = new Handle(this, handle, path, stream)
fileHandle.position = stream.skip(position) // May be != position if the file changed since we saved.
openInputChannel(path) match {
case Some(channel) =>
val fileHandle = new Handle(this, handle, path, channel)
channel.position(position)
handles += handle -> fileHandle
case _ => // The source file seems to have disappeared since last time.
}
@ -62,7 +64,7 @@ trait InputStreamFileSystem extends api.fs.FileSystem {
override def save(nbt: NBTTagCompound) = this.synchronized {
val handlesNbt = new NBTTagList()
for (file <- handles.values) {
assert(!file.isClosed)
assert(file.channel.isOpen)
val handleNbt = new NBTTagCompound()
handleNbt.setInteger("handle", file.handle)
handleNbt.setString("path", file.path)
@ -74,33 +76,65 @@ trait InputStreamFileSystem extends api.fs.FileSystem {
// ----------------------------------------------------------------------- //
protected def openInputStream(path: String): Option[InputStream]
protected def openInputChannel(path: String): Option[SeekableByteChannel]
protected class InputFileChannel(val inputStream: java.io.InputStream) extends SeekableByteChannel {
var isOpen = true
private var position_ = 0L
override def close() = inputStream.close()
override def truncate(size: Long) = throw new java.io.IOException()
override def size() = inputStream.available()
override def position(newPosition: Long) = {
inputStream.reset()
position_ = inputStream.skip(newPosition)
this
}
override def position = position_
override def write(src: ByteBuffer) = throw new java.io.IOException()
override def read(dst: ByteBuffer): Int = {
if (dst.hasArray) {
inputStream.read(dst.array())
}
else {
val count = dst.limit - dst.position
for (i <- 0 until count) {
inputStream.read match {
case -1 => return i
case b => dst.put(b.toByte)
}
}
count
}
}
}
// ----------------------------------------------------------------------- //
private class Handle(val owner: InputStreamFileSystem, val handle: Int, val path: String, val stream: InputStream) extends api.fs.Handle {
var isClosed = false
var position = 0L
private class Handle(val owner: InputStreamFileSystem, val handle: Int, val path: String, val channel: SeekableByteChannel) extends api.fs.Handle {
override def position = channel.position
override def length = owner.size(path)
override def close() = if (!isClosed) {
isClosed = true
override def close() = if (channel.isOpen) {
owner.handles -= handle
stream.close()
channel.close()
}
override def read(into: Array[Byte]) = {
val read = stream.read(into)
if (read >= 0)
position += read
read
channel.read(ByteBuffer.wrap(into))
}
override def seek(to: Long) = {
stream.reset()
position = stream.skip(to)
position
channel.position(to)
channel.position
}
override def write(value: Array[Byte]) = throw new IOException("bad file descriptor")

View File

@ -97,9 +97,9 @@ trait VirtualFileSystem extends OutputStreamFileSystem {
// ----------------------------------------------------------------------- //
protected def openInputStream(path: String) =
protected def openInputChannel(path: String) =
root.get(segments(path)) match {
case Some(obj: VirtualFile) => obj.openInputStream()
case Some(obj: VirtualFile) => obj.openInputStream().map(new InputFileChannel(_))
case _ => None
}

View File

@ -58,8 +58,8 @@ class ZipFileInputStreamFileSystem(private val archive: ArchiveDirectory) extend
// ----------------------------------------------------------------------- //
override protected def openInputStream(path: String) = ZipFileInputStreamFileSystem.synchronized {
entry(path).map(_.openStream())
override protected def openInputChannel(path: String) = ZipFileInputStreamFileSystem.synchronized {
entry(path).map(entry => new InputFileChannel(entry.openStream()))
}
// ----------------------------------------------------------------------- //