/*
* Copyright (c) 2003, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.largeobject;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
/**
* This is an implementation of an InputStream from a large object.
*/
public class BlobInputStream extends InputStream {
/**
* The parent LargeObject
*/
private LargeObject lo;
/**
* The absolute position
*/
private long apos;
/**
* Buffer used to improve performance
*/
private byte[] buffer;
/**
* Position within buffer
*/
private int bpos;
/**
* The buffer size
*/
private int bsize;
/**
* The mark position
*/
private int mpos = 0;
/**
* The limit
*/
private long limit = -1;
/**
* @param lo LargeObject to read from
*/
public BlobInputStream(LargeObject lo) {
this(lo, 1024);
}
/**
* @param lo LargeObject to read from
* @param bsize buffer size
*/
public BlobInputStream(LargeObject lo, int bsize) {
this(lo, bsize, -1);
}
/**
* @param lo LargeObject to read from
* @param bsize buffer size
* @param limit max number of bytes to read
*/
public BlobInputStream(LargeObject lo, int bsize, long limit) {
this.lo = lo;
buffer = null;
bpos = 0;
apos = 0;
this.bsize = bsize;
this.limit = limit;
}
/**
* The minimum required to implement input stream
*/
public int read() throws java.io.IOException {
checkClosed();
try {
if (limit > 0 && apos >= limit) {
return -1;
}
if (buffer == null || bpos >= buffer.length) {
buffer = lo.read(bsize);
bpos = 0;
}
// Handle EOF
if (bpos >= buffer.length) {
return -1;
}
int ret = (buffer[bpos] & 0x7F);
if ((buffer[bpos] & 0x80) == 0x80) {
ret |= 0x80;
}
bpos++;
apos++;
return ret;
} catch (SQLException se) {
throw new IOException(se.toString());
}
}
/**
* Closes this input stream and releases any system resources associated with the stream.
*
* <p>
* The <code>close</code> method of <code>InputStream</code> does nothing.
*
* @throws IOException if an I/O error occurs.
*/
public void close() throws IOException {
if (lo != null) {
try {
lo.close();
lo = null;
} catch (SQLException se) {
throw new IOException(se.toString());
}
}
}
/**
* Marks the current position in this input stream. A subsequent call to the <code>reset</code>
* method repositions this stream at the last marked position so that subsequent reads re-read the
* same bytes.
*
* <p>
* The <code>readlimit</code> arguments tells this input stream to allow that many bytes to be
* read before the mark position gets invalidated.
*
* <p>
* The general contract of <code>mark</code> is that, if the method <code>markSupported</code>
* returns <code>true</code>, the stream somehow remembers all the bytes read after the call to
* <code>mark</code> and stands ready to supply those same bytes again if and whenever the method
* <code>reset</code> is called. However, the stream is not required to remember any data at all
* if more than <code>readlimit</code> bytes are read from the stream before <code>reset</code> is
* called.
*
* <p>
* Marking a closed stream should not have any effect on the stream.
*
* @param readlimit the maximum limit of bytes that can be read before the mark position becomes
* invalid.
* @see java.io.InputStream#reset()
*/
public synchronized void mark(int readlimit) {
try {
mpos = lo.tell();
} catch (SQLException se) {
// Can't throw this because mark API doesn't allow it.
// throw new IOException(se.toString());
}
}
/**
* Repositions this stream to the position at the time the <code>mark</code> method was last
* called on this input stream. NB: If mark is not called we move to the begining.
*
* @see java.io.InputStream#mark(int)
* @see java.io.IOException
*/
public synchronized void reset() throws IOException {
checkClosed();
try {
lo.seek(mpos);
} catch (SQLException se) {
throw new IOException(se.toString());
}
}
/**
* Tests if this input stream supports the <code>mark</code> and <code>reset</code> methods. The
* <code>markSupported</code> method of <code>InputStream</code> returns <code>false</code>.
*
* @return <code>true</code> if this true type supports the mark and reset method;
* <code>false</code> otherwise.
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
public boolean markSupported() {
return true;
}
private void checkClosed() throws IOException {
if (lo == null) {
throw new IOException("BlobOutputStream is closed");
}
}
}