/*
* #%L
* Common package for I/O and related utilities
* %%
* Copyright (C) 2005 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package loci.common;
import java.io.EOFException;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A wrapper for a byte array that implements the IRandomAccess interface.
*
* @see IRandomAccess
*/
public class ByteArrayHandle extends AbstractNIOHandle {
// -- Constants --
/** Initial length of a new file. */
protected static final int INITIAL_LENGTH = 1000000;
// -- Fields --
/** Backing ByteBuffer. */
protected ByteBuffer buffer;
// -- Constructors --
/**
* Creates a random access byte stream to read from, and
* write to, the bytes specified by the byte[] argument.
*/
public ByteArrayHandle(byte[] bytes) {
buffer = ByteBuffer.wrap(bytes);
}
public ByteArrayHandle(ByteBuffer bytes) {
buffer = bytes;
}
/**
* Creates a random access byte stream to read from, and write to.
* @param capacity Number of bytes to initially allocate.
*/
public ByteArrayHandle(int capacity) {
buffer = ByteBuffer.allocate(capacity);
buffer.limit(capacity);
}
/** Creates a random access byte stream to write to a byte array. */
public ByteArrayHandle() {
buffer = ByteBuffer.allocate(INITIAL_LENGTH);
buffer.limit(0);
}
// -- ByteArrayHandle API methods --
/** Gets the byte array backing this FileHandle. */
public byte[] getBytes() {
return buffer.array();
}
/**
* Gets the byte buffer backing this handle. <b>NOTE:</b> This is the
* backing buffer. Any modifications to this buffer including position,
* length and capacity will affect subsequent calls upon its source handle.
* @return Backing buffer of this handle.
*/
public ByteBuffer getByteBuffer() {
return buffer;
}
// -- AbstractNIOHandle API methods --
/* @see AbstractNIOHandle.setLength(long) */
@Override
public void setLength(long length) throws IOException {
if (length > buffer.capacity()) {
long fp = getFilePointer();
ByteBuffer tmp = ByteBuffer.allocate((int) (length * 2));
ByteOrder order = buffer == null ? null : getOrder();
seek(0);
buffer = tmp.put(buffer);
if (order != null) setOrder(order);
seek(fp);
}
buffer.limit((int) length);
}
// -- IRandomAccess API methods --
/* @see IRandomAccess.close() */
@Override
public void close() {
}
/* @see IRandomAccess.getFilePointer() */
@Override
public long getFilePointer() {
return buffer.position();
}
/* @see IRandomAccess.length() */
@Override
public long length() {
return buffer.limit();
}
/* @see IRandomAccess.read(byte[]) */
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
/* @see IRandomAccess.read(byte[], int, int) */
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (getFilePointer() + len > length()) {
len = (int) (length() - getFilePointer());
}
buffer.get(b, off, len);
return len;
}
/* @see IRandomAccess.read(ByteBuffer) */
@Override
public int read(ByteBuffer buf) throws IOException {
return read(buf, 0, buf.capacity());
}
/* @see IRandomAccess.read(ByteBuffer, int, int) */
@Override
public int read(ByteBuffer buf, int off, int len) throws IOException {
if (buf.hasArray()) {
buffer.get(buf.array(), off, len);
return len;
}
byte[] b = new byte[len];
read(b);
buf.put(b, 0, len);
return len;
}
/* @see IRandomAccess.seek(long) */
@Override
public void seek(long pos) throws IOException {
if (pos > length()) {
setLength(pos);
}
buffer.position((int) pos);
}
/* @see IRandomAccess.getOrder() */
@Override
public ByteOrder getOrder() {
return buffer.order();
}
/* @see IRandomAccess.setOrder(ByteOrder) */
@Override
public void setOrder(ByteOrder order) {
buffer.order(order);
}
// -- DataInput API methods --
/* @see java.io.DataInput.readBoolean() */
@Override
public boolean readBoolean() throws IOException {
return readByte() != 0;
}
/* @see java.io.DataInput.readByte() */
@Override
public byte readByte() throws IOException {
if (getFilePointer() + 1 > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
return buffer.get();
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readChar() */
@Override
public char readChar() throws IOException {
if (getFilePointer() + 2 > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
return buffer.getChar();
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readDouble() */
@Override
public double readDouble() throws IOException {
if (getFilePointer() + 8 > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
return buffer.getDouble();
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readFloat() */
@Override
public float readFloat() throws IOException {
if (getFilePointer() + 4 > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
return buffer.getFloat();
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readFully(byte[]) */
@Override
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
/* @see java.io.DataInput.readFully(byte[], int, int) */
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
if (getFilePointer() + len > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
buffer.get(b, off, len);
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readInt() */
@Override
public int readInt() throws IOException {
if (getFilePointer() + 4 > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
return buffer.getInt();
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readLine() */
@Override
public String readLine() throws IOException {
throw new IOException("Unimplemented");
}
/* @see java.io.DataInput.readLong() */
@Override
public long readLong() throws IOException {
if (getFilePointer() + 8 > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
return buffer.getLong();
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readShort() */
@Override
public short readShort() throws IOException {
if (getFilePointer() + 2 > length()) {
throw new EOFException(EOF_ERROR_MSG);
}
try {
return buffer.getShort();
}
catch (BufferUnderflowException e) {
EOFException eof = new EOFException();
eof.initCause(e);
throw eof;
}
}
/* @see java.io.DataInput.readUnsignedByte() */
@Override
public int readUnsignedByte() throws IOException {
return readByte() & 0xff;
}
/* @see java.io.DataInput.readUnsignedShort() */
@Override
public int readUnsignedShort() throws IOException {
return readShort() & 0xffff;
}
/* @see java.io.DataInput.readUTF() */
@Override
public String readUTF() throws IOException {
int length = readUnsignedShort();
byte[] b = new byte[length];
read(b);
return new String(b, Constants.ENCODING);
}
/* @see java.io.DataInput.skipBytes(int) */
@Override
public int skipBytes(int n) throws IOException {
int skipped = (int) Math.min(n, length() - getFilePointer());
if (skipped < 0) {
return 0;
}
seek(getFilePointer() + skipped);
return skipped;
}
// -- DataOutput API methods --
/* @see java.io.DataOutput.write(byte[]) */
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
/* @see java.io.DataOutput.write(byte[], int, int) */
@Override
public void write(byte[] b, int off, int len) throws IOException {
validateLength(len);
buffer.put(b, off, len);
}
/* @see IRandomAccess.write(ByteBuffer) */
@Override
public void write(ByteBuffer buf) throws IOException {
write(buf, 0, buf.capacity());
}
/* @see IRandomAccess.write(ByteBuffer, int, int) */
@Override
public void write(ByteBuffer buf, int off, int len) throws IOException {
validateLength(len);
buf.position(off);
buf.limit(off + len);
buffer.put(buf);
}
/* @see java.io.DataOutput.write(int b) */
@Override
public void write(int b) throws IOException {
validateLength(1);
buffer.put((byte) b);
}
/* @see java.io.DataOutput.writeBoolean(boolean) */
@Override
public void writeBoolean(boolean v) throws IOException {
write(v ? 1 : 0);
}
/* @see java.io.DataOutput.writeByte(int) */
@Override
public void writeByte(int v) throws IOException {
write(v);
}
/* @see java.io.DataOutput.writeBytes(String) */
@Override
public void writeBytes(String s) throws IOException {
write(s.getBytes(Constants.ENCODING));
}
/* @see java.io.DataOutput.writeChar(int) */
@Override
public void writeChar(int v) throws IOException {
validateLength(2);
buffer.putChar((char) v);
}
/* @see java.io.DataOutput.writeChars(String) */
@Override
public void writeChars(String s) throws IOException {
int len = 2 * s.length();
validateLength(len);
char[] c = s.toCharArray();
for (int i=0; i<c.length; i++) {
writeChar(c[i]);
}
}
/* @see java.io.DataOutput.writeDouble(double) */
@Override
public void writeDouble(double v) throws IOException {
validateLength(8);
buffer.putDouble(v);
}
/* @see java.io.DataOutput.writeFloat(float) */
@Override
public void writeFloat(float v) throws IOException {
validateLength(4);
buffer.putFloat(v);
}
/* @see java.io.DataOutput.writeInt(int) */
@Override
public void writeInt(int v) throws IOException {
validateLength(4);
buffer.putInt(v);
}
/* @see java.io.DataOutput.writeLong(long) */
@Override
public void writeLong(long v) throws IOException {
validateLength(8);
buffer.putLong(v);
}
/* @see java.io.DataOutput.writeShort(int) */
@Override
public void writeShort(int v) throws IOException {
validateLength(2);
buffer.putShort((short) v);
}
/* @see java.io.DataOutput.writeUTF(String) */
@Override
public void writeUTF(String str) throws IOException {
byte[] b = str.getBytes(Constants.ENCODING);
writeShort(b.length);
write(b);
}
}