mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-19 12:17:17 -04:00
fixed some issues with file:seek; added a virtual file system implementation that keeps all files in memory, used for a volatile /tmp mount in computers for now (512KB of writable memory that gets wiped on reboot)
This commit is contained in:
parent
14bdba937d
commit
9459366a06
@ -435,16 +435,14 @@ function file:seek(whence, offset)
|
||||
checkArg(2, offset, "number")
|
||||
assert(math.floor(offset) == offset, "bad argument #2 (not an integer)")
|
||||
|
||||
if whence == "cur" and offset ~= 0 then
|
||||
offset = offset - #(self.buffer or "")
|
||||
local result, reason
|
||||
if whence == "cur" then
|
||||
result, reason = self.stream:seek(whence, offset - #self.buffer)
|
||||
else
|
||||
result, reason = self.stream:seek(whence, offset)
|
||||
end
|
||||
local result, reason = self.stream:seek(whence, offset)
|
||||
if result then
|
||||
if offset ~= 0 then
|
||||
self.buffer = ""
|
||||
elseif whence == "cur" then
|
||||
result = result - #self.buffer
|
||||
end
|
||||
return result
|
||||
else
|
||||
return nil, reason
|
||||
|
@ -95,7 +95,8 @@ local sandbox = {
|
||||
freeMemory = os.freeMemory,
|
||||
totalMemory = os.totalMemory,
|
||||
address = os.address,
|
||||
romAddress = os.romAddress
|
||||
romAddress = os.romAddress,
|
||||
tmpAddress = os.tmpAddress
|
||||
},
|
||||
|
||||
string = {
|
||||
@ -165,6 +166,7 @@ end
|
||||
local function main(args)
|
||||
local function init()
|
||||
sandbox.driver.filesystem.mount(os.romAddress(), "/")
|
||||
sandbox.driver.filesystem.mount(os.tmpAddress(), "/tmp")
|
||||
local result, reason = sandbox.loadfile("/boot/init.lua")
|
||||
if not result then
|
||||
error(reason, 0)
|
||||
|
@ -23,7 +23,10 @@ end
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function onComponentAdded(_, address)
|
||||
if component.type(address) == "filesystem" and address ~= os.romAddress() then
|
||||
if component.type(address) == "filesystem" and
|
||||
address ~= os.romAddress() and
|
||||
address ~= os.tmpAddress()
|
||||
then
|
||||
local name = address:sub(1, 3)
|
||||
repeat
|
||||
name = address:sub(1, name:len() + 1)
|
||||
|
@ -333,7 +333,7 @@ function term.read(history)
|
||||
event.listen("key_up", onKeyUp)
|
||||
event.listen("clipboard", onClipboard)
|
||||
term.cursorBlink(true)
|
||||
while not result do
|
||||
while term.isAvailable() and not result do
|
||||
coroutine.sleep()
|
||||
end
|
||||
if keyRepeat then
|
||||
|
@ -267,6 +267,20 @@ object FileSystem extends FileSystemAPI {
|
||||
def fromSaveDir(root: String, capacity: Long) =
|
||||
instance.fold(None: Option[FileSystem])(_.fromSaveDir(root, capacity))
|
||||
|
||||
/**
|
||||
* Creates a new *writable* file system that resides in memory.
|
||||
* <p/>
|
||||
* Any contents created and written on this file system will be lost when
|
||||
* the node is removed from the network.
|
||||
* <p/>
|
||||
* This is used for computers' `/tmp` mount, for example.
|
||||
*
|
||||
* @param capacity the capacity of the file system.
|
||||
* @return a file system residing in memory.
|
||||
*/
|
||||
def fromRam(capacity: Long): Option[FileSystem] =
|
||||
instance.fold(None: Option[FileSystem])(_.fromRam(capacity))
|
||||
|
||||
/**
|
||||
* Creates a network node that makes the specified file system available via
|
||||
* the common file system driver.
|
||||
|
@ -9,5 +9,7 @@ trait FileSystemAPI {
|
||||
|
||||
def fromSaveDir(root: String, capacity: Long): Option[FileSystem]
|
||||
|
||||
def fromRam(capacity: Long): Option[FileSystem]
|
||||
|
||||
def asNode(fs: FileSystem): Option[Node]
|
||||
}
|
@ -68,6 +68,10 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
fromClass(OpenComputers.getClass, Config.resourceDomain, "lua/rom").
|
||||
flatMap(api.FileSystem.asNode)
|
||||
|
||||
private val tmp = api.FileSystem.
|
||||
fromRam(512 * 1024).
|
||||
flatMap(api.FileSystem.asNode)
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private var timeStarted = 0L // Game-world time [ms] for os.uptime().
|
||||
@ -159,13 +163,19 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
// Update world time for computer threads.
|
||||
worldTime = owner.world.getWorldInfo.getWorldTotalTime
|
||||
|
||||
// Signal stops to the network. This is used to close file handles, for example.
|
||||
if (wasRunning && !isRunning) {
|
||||
def cleanup() {
|
||||
rom.foreach(rom => rom.network.foreach(_.remove(rom)))
|
||||
tmp.foreach(tmp => tmp.network.foreach(_.remove(tmp)))
|
||||
owner.network.foreach(_.sendToVisible(owner, "computer.stopped"))
|
||||
// Clear any screens we use while we're at it.
|
||||
owner.network.foreach(_.sendToNeighbors(owner, "gpu.fill",
|
||||
1.0, 1.0, Double.PositiveInfinity, Double.PositiveInfinity, " ".getBytes("UTF-8")))
|
||||
}
|
||||
|
||||
// Signal stops to the network. This is used to close file handles, for example.
|
||||
if (wasRunning && !isRunning) {
|
||||
cleanup()
|
||||
}
|
||||
wasRunning = isRunning
|
||||
|
||||
// If there was an error message (i.e. the computer crashed) display it on
|
||||
@ -184,8 +194,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
// Computer is rebooting.
|
||||
case Computer.State.Rebooting => {
|
||||
state = Computer.State.Stopped
|
||||
owner.network.foreach(_.sendToVisible(owner, "computer.stopped"))
|
||||
owner.network.foreach(_.sendToVisible(owner, "computer.started"))
|
||||
cleanup()
|
||||
start()
|
||||
}
|
||||
// Resume from pauses based on signal underflow.
|
||||
@ -290,6 +299,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
}).asJava)
|
||||
|
||||
rom.foreach(_.load(nbt.getCompoundTag("rom")))
|
||||
tmp.foreach(_.load(nbt.getCompoundTag("tmp")))
|
||||
kernelMemory = nbt.getInteger("kernelMemory")
|
||||
timeStarted = nbt.getLong("timeStarted")
|
||||
cpuTime = nbt.getLong("cpuTime")
|
||||
@ -373,6 +383,9 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
val romNbt = new NBTTagCompound()
|
||||
rom.foreach(_.save(romNbt))
|
||||
nbt.setCompoundTag("rom", romNbt)
|
||||
val tmpNbt = new NBTTagCompound()
|
||||
tmp.foreach(_.save(tmpNbt))
|
||||
nbt.setCompoundTag("tmp", tmpNbt)
|
||||
nbt.setInteger("kernelMemory", kernelMemory)
|
||||
nbt.setLong("timeStarted", timeStarted)
|
||||
nbt.setLong("cpuTime", cpuTime)
|
||||
@ -505,6 +518,16 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
})
|
||||
lua.setField(-2, "romAddress")
|
||||
|
||||
// And it's /tmp address...
|
||||
lua.pushScalaFunction(lua => {
|
||||
tmp.foreach(_.address match {
|
||||
case None => lua.pushNil()
|
||||
case Some(address) => lua.pushString(address)
|
||||
})
|
||||
1
|
||||
})
|
||||
lua.setField(-2, "tmpAddress")
|
||||
|
||||
// Pop the os table.
|
||||
lua.pop(1)
|
||||
|
||||
@ -630,8 +653,9 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
// Clear any left-over signals from a previous run.
|
||||
signals.clear()
|
||||
|
||||
// Connect the ROM node to our owner.
|
||||
// Connect the ROM and `/tmp` node to our owner.
|
||||
rom.foreach(rom => owner.network.foreach(_.connect(owner, rom)))
|
||||
tmp.foreach(tmp => owner.network.foreach(_.connect(owner, tmp)))
|
||||
|
||||
return true
|
||||
}
|
||||
@ -859,11 +883,13 @@ object Computer {
|
||||
override protected def onConnect() {
|
||||
super.onConnect()
|
||||
computer.rom.foreach(rom => network.foreach(_.connect(this, rom)))
|
||||
computer.tmp.foreach(tmp => network.foreach(_.connect(this, tmp)))
|
||||
}
|
||||
|
||||
override protected def onDisconnect() {
|
||||
super.onDisconnect()
|
||||
computer.rom.foreach(rom => rom.network.foreach(_.remove(rom)))
|
||||
computer.tmp.foreach(tmp => tmp.network.foreach(_.remove(tmp)))
|
||||
}
|
||||
|
||||
override def load(nbt: NBTTagCompound) {
|
||||
|
@ -108,7 +108,7 @@ class FileSystem(val fileSystem: api.FileSystem) extends Node {
|
||||
fileSystem.file(handle.toInt) match {
|
||||
case None => None
|
||||
case Some(file) =>
|
||||
if (offset.toInt != 0) new String(whence, "UTF-8") match {
|
||||
new String(whence, "UTF-8") match {
|
||||
case "cur" => file.seek(file.position + offset.toInt)
|
||||
case "set" => file.seek(offset.toLong)
|
||||
case "end" => file.seek(file.length + offset.toInt)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package li.cil.oc.server.fs
|
||||
|
||||
import java.io
|
||||
import java.io.{FileNotFoundException, FileInputStream}
|
||||
import li.cil.oc.api
|
||||
|
||||
trait FileInputStreamFileSystem extends api.FileSystem with InputStreamFileSystem {
|
||||
@ -18,15 +17,15 @@ trait FileInputStreamFileSystem extends api.FileSystem with InputStreamFileSyste
|
||||
|
||||
override def isDirectory(path: String) = new io.File(root, path).isDirectory
|
||||
|
||||
override def list(path: String) = new io.File(root, path) match {
|
||||
override def list(path: String): Option[Array[String]] = new io.File(root, path) match {
|
||||
case file if file.exists() && file.isFile => Some(Array(file.getName))
|
||||
case directory if directory.exists() && directory.isDirectory => Some(directory.listFiles().
|
||||
map(file => if (file.isDirectory) file.getName + "/" else file.getName))
|
||||
case _ => throw new FileNotFoundException("no such file or directory")
|
||||
case _ => throw new io.FileNotFoundException("no such file or directory")
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override protected def openInputStream(path: String) =
|
||||
Some(new FileInputStream(new io.File(root, path)))
|
||||
override protected def openInputStream(path: String): Option[io.InputStream] =
|
||||
Some(new io.FileInputStream(new io.File(root, path)))
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ object FileSystem extends api.detail.FileSystemAPI {
|
||||
else None
|
||||
}
|
||||
|
||||
override def fromRam(capacity: Long): Option[api.FileSystem] = Some(new RamFileSystem(capacity))
|
||||
|
||||
override def asNode(fileSystem: api.FileSystem) = Some(new component.FileSystem(fileSystem))
|
||||
|
||||
private class ReadOnlyFileSystem(protected val root: io.File)
|
||||
@ -57,4 +59,8 @@ object FileSystem extends api.detail.FileSystemAPI {
|
||||
with FileOutputStreamFileSystem
|
||||
with Capacity
|
||||
|
||||
private class RamFileSystem(protected val capacity: Long)
|
||||
extends VirtualFileSystem
|
||||
with Capacity
|
||||
|
||||
}
|
||||
|
@ -87,7 +87,8 @@ trait InputStreamFileSystem extends api.FileSystem {
|
||||
|
||||
def seek(to: Long) = {
|
||||
stream.reset()
|
||||
stream.skip(to)
|
||||
position = stream.skip(to)
|
||||
position
|
||||
}
|
||||
|
||||
def write(value: Array[Byte]) = throw new IOException("bad file descriptor")
|
||||
|
331
li/cil/oc/server/fs/VirtualFileSystem.scala
Normal file
331
li/cil/oc/server/fs/VirtualFileSystem.scala
Normal file
@ -0,0 +1,331 @@
|
||||
package li.cil.oc.server.fs
|
||||
|
||||
import java.io
|
||||
import li.cil.oc.api.fs.Mode
|
||||
import net.minecraft.nbt.{NBTTagList, NBTTagCompound}
|
||||
import scala.collection.mutable
|
||||
|
||||
class VirtualFileSystem extends OutputStreamFileSystem {
|
||||
private val root = new VirtualDirectory
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def exists(path: String) =
|
||||
root.exists(segments(path))
|
||||
|
||||
override def isDirectory(path: String) =
|
||||
root.isDirectory(segments(path))
|
||||
|
||||
override def size(path: String) =
|
||||
root.size(segments(path))
|
||||
|
||||
override def list(path: String) =
|
||||
root.list(segments(path))
|
||||
|
||||
override def rename(from: String, to: String) =
|
||||
if (from != "" && exists(from) && !exists(to)) {
|
||||
root.get(segments(to).dropRight(1)) match {
|
||||
case Some(toParent: VirtualDirectory) =>
|
||||
val fromParent = root.get(segments(from).dropRight(1)).get.asInstanceOf[VirtualDirectory]
|
||||
val fromName = segments(from).last
|
||||
val toName = segments(to).last
|
||||
val obj = fromParent.children(fromName)
|
||||
fromParent.children -= fromName
|
||||
toParent.children += toName -> obj
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
else false
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override protected def makeDirectory(path: String) =
|
||||
root.makeDirectory(segments(path))
|
||||
|
||||
override protected def delete(path: String) =
|
||||
root.delete(segments(path))
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def close() = {
|
||||
super.close()
|
||||
root.children.clear()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override protected def openInputStream(path: String) =
|
||||
root.openInputStream(segments(path))
|
||||
|
||||
override protected def openOutputStream(path: String, mode: Mode.Value) =
|
||||
root.openOutputStream(segments(path), mode)
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def load(nbt: NBTTagCompound) = {
|
||||
root.load(nbt)
|
||||
super.load(nbt) // Last to ensure streams can be re-opened.
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound) = {
|
||||
super.save(nbt) // First to allow flushing.
|
||||
root.save(nbt)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private def segments(path: String) = path.split("/").view.filter(_ != "")
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private abstract class VirtualObject {
|
||||
def exists(path: Iterable[String]) = path.isEmpty
|
||||
|
||||
def isDirectory(path: Iterable[String] = Iterable.empty[String]): Boolean
|
||||
|
||||
def size(path: Iterable[String]): Long
|
||||
|
||||
def list(path: Iterable[String]): Option[Array[String]]
|
||||
|
||||
def makeDirectory(path: Iterable[String]): Boolean
|
||||
|
||||
def delete(path: Iterable[String]): Boolean
|
||||
|
||||
def canDelete: Boolean
|
||||
|
||||
def openInputStream(path: Iterable[String]): Option[io.InputStream]
|
||||
|
||||
def openOutputStream(path: Iterable[String], mode: Mode.Value): Option[io.OutputStream]
|
||||
|
||||
def load(nbt: NBTTagCompound)
|
||||
|
||||
def save(nbt: NBTTagCompound)
|
||||
|
||||
def get(path: Iterable[String]): Option[VirtualObject] = if (path.isEmpty) Some(this) else None
|
||||
}
|
||||
|
||||
private class VirtualFile extends VirtualObject {
|
||||
var data = Array.empty[Byte]
|
||||
|
||||
var stream: Option[VirtualFileOutputStream] = None
|
||||
|
||||
override def isDirectory(path: Iterable[String]) = false
|
||||
|
||||
override def size(path: Iterable[String]) = data.length
|
||||
|
||||
override def list(path: Iterable[String]) = None
|
||||
|
||||
override def makeDirectory(path: Iterable[String]) = false
|
||||
|
||||
override def delete(path: Iterable[String]) = false
|
||||
|
||||
override def canDelete = stream.isEmpty
|
||||
|
||||
override def openInputStream(path: Iterable[String]) =
|
||||
if (path.isEmpty) Some(new VirtualFileInputStream(this))
|
||||
else None
|
||||
|
||||
override def openOutputStream(path: Iterable[String], mode: Mode.Value) =
|
||||
if (path.isEmpty) {
|
||||
if (stream.isDefined) throw new io.FileNotFoundException()
|
||||
if (mode == Mode.Write)
|
||||
data = Array.empty[Byte]
|
||||
stream = Some(new VirtualFileOutputStream(this))
|
||||
stream
|
||||
}
|
||||
else None
|
||||
|
||||
override def load(nbt: NBTTagCompound) {
|
||||
data = nbt.getByteArray("data")
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound) {
|
||||
nbt.setByteArray("data", data)
|
||||
}
|
||||
}
|
||||
|
||||
private class VirtualDirectory extends VirtualObject {
|
||||
val children = mutable.Map.empty[String, VirtualObject]
|
||||
|
||||
override def exists(path: Iterable[String]) = super.exists(path) || (children.get(path.head) match {
|
||||
case Some(child) => child.exists(path.drop(1))
|
||||
case _ => false
|
||||
})
|
||||
|
||||
override def isDirectory(path: Iterable[String]) = path.isEmpty || (children.get(path.head) match {
|
||||
case Some(child) => child.isDirectory(path.drop(1))
|
||||
case _ => false
|
||||
})
|
||||
|
||||
override def size(path: Iterable[String]) =
|
||||
if (path.isEmpty) 0
|
||||
else children.get(path.head) match {
|
||||
case Some(child) => child.size(path.drop(1))
|
||||
case _ => 0
|
||||
}
|
||||
|
||||
override def list(path: Iterable[String]) =
|
||||
if (path.isEmpty) Some(children.map {
|
||||
case (childName, child) => if (child.isDirectory()) childName + "/" else childName
|
||||
}.toArray)
|
||||
else children.get(path.head) match {
|
||||
case Some(child) => child.list(path.drop(1))
|
||||
case _ => None
|
||||
}
|
||||
|
||||
override def makeDirectory(path: Iterable[String]) =
|
||||
if (path.size == 1) {
|
||||
val directory = path.head
|
||||
if (children.contains(directory)) false
|
||||
else {
|
||||
children += directory -> new VirtualDirectory
|
||||
true
|
||||
}
|
||||
} else children.get(path.head) match {
|
||||
case Some(child) => child.makeDirectory(path.drop(1))
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def delete(path: Iterable[String]) =
|
||||
if (path.size == 1) {
|
||||
val childName = path.head
|
||||
children.get(childName) match {
|
||||
case Some(child) if child.canDelete =>
|
||||
children -= childName
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
} else children.get(path.head) match {
|
||||
case Some(child) => child.delete(path.drop(1))
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def canDelete = children.isEmpty
|
||||
|
||||
override def openInputStream(path: Iterable[String]) =
|
||||
if (path.isEmpty) None
|
||||
else children.get(path.head) match {
|
||||
case Some(child) => child.openInputStream(path.drop(1))
|
||||
case _ => None
|
||||
}
|
||||
|
||||
override def openOutputStream(path: Iterable[String], mode: Mode.Value) =
|
||||
if (path.isEmpty) None
|
||||
else children.get(path.head) match {
|
||||
case Some(child) => child.openOutputStream(path.drop(1), mode)
|
||||
case None if path.size == 1 =>
|
||||
val childName = path.head
|
||||
val child = new VirtualFile
|
||||
children += childName -> child
|
||||
child.openOutputStream(Array.empty[String], mode)
|
||||
case _ => None
|
||||
}
|
||||
|
||||
override def load(nbt: NBTTagCompound) {
|
||||
val childrenNbt = nbt.getTagList("children")
|
||||
(0 until childrenNbt.tagCount).map(childrenNbt.tagAt).map(_.asInstanceOf[NBTTagCompound]).foreach(childNbt => {
|
||||
val child =
|
||||
if (childNbt.getBoolean("isDirectory")) new VirtualDirectory
|
||||
else new VirtualFile
|
||||
child.load(childNbt)
|
||||
children += childNbt.getString("name") -> child
|
||||
})
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound) {
|
||||
val childrenNbt = new NBTTagList()
|
||||
for ((childName, child) <- children) {
|
||||
val childNbt = new NBTTagCompound()
|
||||
childNbt.setBoolean("isDirectory", child.isDirectory())
|
||||
childNbt.setString("name", childName)
|
||||
child.save(childNbt)
|
||||
childrenNbt.appendTag(childNbt)
|
||||
}
|
||||
nbt.setTag("children", childrenNbt)
|
||||
}
|
||||
|
||||
override def get(path: Iterable[String]) =
|
||||
super.get(path) orElse {
|
||||
children.get(path.head) match {
|
||||
case Some(child) => child.get(path.drop(1))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private class VirtualFileInputStream(val file: VirtualFile) extends io.InputStream {
|
||||
private var isClosed = false
|
||||
|
||||
private var position = 0
|
||||
|
||||
override def available() =
|
||||
if (isClosed) 0
|
||||
else (file.data.length - position) max 0
|
||||
|
||||
override def close() = isClosed = true
|
||||
|
||||
def read() =
|
||||
if (!isClosed) {
|
||||
if (available == 0) -1
|
||||
else {
|
||||
position += 1
|
||||
file.data(position - 1)
|
||||
}
|
||||
}
|
||||
else throw new io.IOException("file is closed")
|
||||
|
||||
override def read(b: Array[Byte], off: Int, len: Int) =
|
||||
if (!isClosed) {
|
||||
if (available == 0) -1
|
||||
else {
|
||||
val n = len min available
|
||||
Array.copy(file.data, position, b, off, n)
|
||||
position += n
|
||||
n
|
||||
}
|
||||
}
|
||||
else throw new io.IOException("file is closed")
|
||||
|
||||
override def reset() =
|
||||
if (!isClosed) {
|
||||
position = 0
|
||||
}
|
||||
else throw new io.IOException("file is closed")
|
||||
|
||||
override def skip(n: Long) =
|
||||
if (!isClosed) {
|
||||
position = ((position + n) min Int.MaxValue).toInt
|
||||
position
|
||||
}
|
||||
else throw new io.IOException("file is closed")
|
||||
}
|
||||
|
||||
private class VirtualFileOutputStream(val file: VirtualFile) extends io.ByteArrayOutputStream {
|
||||
private var isClosed = false
|
||||
|
||||
override def close() = if (!isClosed) {
|
||||
flush()
|
||||
isClosed = true
|
||||
file.stream = None
|
||||
}
|
||||
|
||||
override def flush() =
|
||||
if (!isClosed) {
|
||||
file.data ++= toByteArray
|
||||
reset()
|
||||
} else throw new io.IOException("file is closed")
|
||||
|
||||
override def write(b: Array[Byte], off: Int, len: Int) =
|
||||
if (!isClosed) super.write(b, off, len)
|
||||
else throw new io.IOException("file is closed")
|
||||
|
||||
override def write(b: Int) =
|
||||
if (!isClosed) super.write(b)
|
||||
else throw new io.IOException("file is closed")
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user