An implementation of a virtual file, whose contents are kept in memory
/**
* The utillib library.
* More information is available at http://www.jinchess.com/.
* Copyright (C) 2002 Alexander Maryanovsky.
* All rights reserved.
*
* The utillib library is free software; you can redistribute
* it and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The utillib library is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with utillib library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*/
import java.io.*;
/**
* An implementation of a virtual file, whose contents are kept in memory.
*/
public class MemoryFile{
/**
* The default buffer size.
*/
private static final int DEFAULT_BUFFER_SIZE = 2048;
/**
* The initial buffer size.
*/
private final int initBufSize;
/**
* The data byte array.
*/
private byte [] data = null;
/**
* The amount of bytes that have been written into the file.
*/
private volatile int size = 0;
/**
* The currently open <code>OutputStream</code> writing into this
* <code>MemoryFile</code>. <code>null</code> if none.
*/
private OutputStream out = null;
/**
* Creates a new, empty, <code>MemoryFile</code>.
*/
public MemoryFile(){
this(null, DEFAULT_BUFFER_SIZE);
}
/**
* Creates a new empty <code>MemoryFile</code> with the specified initial
* buffer size.
*/
public MemoryFile(int bufSize){
this(null, bufSize);
}
/**
* Creates a new <code>MemoryFile</code> initialized with the data from the
* specified byte array. The specified byte array is copied.
*/
public MemoryFile(byte [] data){
this(data, DEFAULT_BUFFER_SIZE);
}
/**
* Creates a new <code>MemoryFile</code> initialized with the data from the
* specified byte array and the initial buffer size. Note that if the
* specified initial buffer size is smaller than the length of the byte array,
* it will only be user when/if the contents of the file are cleared.
*/
public MemoryFile(byte [] data, int bufSize){
if (bufSize <= 0)
throw new IllegalArgumentException("The buffer size must be a positive integer");
this.initBufSize = bufSize;
if (data != null){
this.data = new byte[bufSize > data.length ? bufSize : data.length];
System.arraycopy(data, 0, this.data, 0, data.length);
this.size = data.length;
}
}
/**
* Returns the size of the file.
*/
public int getSize(){
return size;
}
/**
* Returns an <code>OutputStream</code> that will write into this
* <code>MemoryFile</code>. Only a single open <code>OutputStream</code> is
* allowed per <code>MemoryFile</code>. Note that invoking this method clears
* the current contents of the file.
*
* @throws IOException if an open OutputStream writing into this
* <code>MemoryFile</code> already exists.
*/
public synchronized OutputStream getOutputStream() throws IOException{
if (out != null)
throw new IOException("MemoryFile already open for writing");
size = 0;
data = new byte[initBufSize];
return out = new InternalOutputStream();
}
/**
* Returns an <code>InputStream</code> that will read from this
* <code>MemoryFile</code>. Note that the data read from the returned
* <code>InputStream</code> will be the data in the file when this method is
* invoked. If more data is written into the file or the contents of the file
* are cleared, the returned <code>InputStream</code> will not be affected.
*/
public InputStream getInputStream(){
return new ByteArrayInputStream(data, 0, getSize());
}
/**
* Clears this file, resetting its size to 0.
*
* @throws IOException if the file is currently open for writing.
*/
public synchronized void clear() throws IOException{
if (out != null)
throw new IOException("MemoryFile open for writing");
size = 0;
}
/**
* Writes the contents of this <code>MemoryFile</code> into the specified
* <code>OutputStream</code>.
*/
public synchronized void writeTo(OutputStream out) throws IOException{
out.write(data, 0, getSize());
}
/**
* Increases the size of the internal buffer by at least the specified amount
* of bytes. The caller must take care of proper synchronization.
*/
private void growBuf(int minGrowSize){
int growSize = minGrowSize < data.length ? data.length : minGrowSize;
byte [] newData = new byte[data.length + growSize];
System.arraycopy(data, 0, newData, 0, data.length);
data = newData;
}
/**
* Writes a single byte into the file.
*/
private synchronized void write(int b){
if (data.length - size == 0)
growBuf(1);
data[size++] = (byte)(b&0xff);
}
/**
* Writes the specified amount of bytes from the specified array starting with
* the specified index into the file.
*/
private synchronized void write(byte [] arr, int offset, int length){
if (data.length - size < arr.length)
growBuf(arr.length + size - data.length);
System.arraycopy(arr, offset, data, size, length);
size += length;
}
/**
* Closes the <code>OutputStream</code> writing into this file.
*
* @throws IOException if the <code>OutputStream</code> is already closed.
*/
private synchronized void closeOutputStream() throws IOException{
if (out == null)
throw new IOException("OutputStream already closed");
out = null;
}
/**
* The <code>OutputStream</code> class that is responsible for writing into
* this <code>MemoryFile</code>. This class simply forwards the calls to the
* methods in its outer class.
*/
private class InternalOutputStream extends OutputStream{
public void write(int b){
MemoryFile.this.write(b);
}
public void write(byte [] buf, int offset, int length){
MemoryFile.this.write(buf, offset, length);
}
public void close() throws IOException{
MemoryFile.this.closeOutputStream();
}
}
}
Related examples in the same category