org.sqlite.Blob Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sqlite-javacpp Show documentation
Show all versions of sqlite-javacpp Show documentation
JDBC Driver for SQLite using JavaCPP
The newest version!
/*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
package org.sqlite;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import static org.sqlite.SQLite.SQLITE_OK;
import static org.sqlite.SQLite.nativeString;
import static org.sqlite.SQLite.sqlite3_blob;
import static org.sqlite.SQLite.sqlite3_blob_bytes;
import static org.sqlite.SQLite.sqlite3_blob_close;
import static org.sqlite.SQLite.sqlite3_blob_read;
import static org.sqlite.SQLite.sqlite3_blob_reopen;
import static org.sqlite.SQLite.sqlite3_blob_write;
import static org.sqlite.SQLite.sqlite3_log;
/**
* A Handle To An Open BLOB
* sqlite3_blob
*/
public class Blob implements AutoCloseable {
private final Conn c;
private sqlite3_blob pBlob;
private int readOffset;
private int writeOffset;
private int size = -1;
Blob(Conn c, sqlite3_blob pBlob) {
assert c != null;
this.c = c;
this.pBlob = pBlob;
}
/**
* @return the size of an opened BLOB
* sqlite3_blob_bytes
*/
public int getBytes() throws SQLiteException {
checkOpen();
if (size < 0) {
size = sqlite3_blob_bytes(pBlob);
}
return size;
}
/**
* Read data from this BLOB incrementally
* @param b buffer to read into
* @return number of bytes read
* sqlite3_blob_read
*/
public int read(ByteBuffer b) throws SQLiteException {
if (b == null) {
throw new NullPointerException();
}
checkOpen();
final int n = b.remaining();
final int res = sqlite3_blob_read(pBlob, b, n, readOffset);
if (res != SQLITE_OK) {
throw new SQLiteException(c, "error while reading blob", res);
}
readOffset += n;
return n;
}
/**
* Write data into this BLOB incrementally
* @param b bytes to write
* @return number of bytes written
* sqlite3_blob_write
*/
public int write(ByteBuffer b) throws SQLiteException {
if (b == null) {
throw new NullPointerException();
}
checkOpen();
final int n = b.remaining();
final int res = sqlite3_blob_write(pBlob, b, n, writeOffset);
if (res != SQLITE_OK) {
throw new SQLiteException(c, "error while writing blob", res);
}
writeOffset += n;
return n;
}
/**
* Move this BLOB handle to a new row
* @param iRow new row id
* @throws SQLiteException if this BLOB is closed
* sqlite3_blob_reopen
*/
public void reopen(long iRow) throws SQLiteException {
checkOpen();
final int res = sqlite3_blob_reopen(pBlob, iRow);
if (res != SQLITE_OK) {
throw new SQLiteException(c, "error while reopening blob", res);
}
readOffset = 0;
writeOffset = 0;
size = -1;
}
@Override
protected void finalize() throws Throwable {
if (pBlob != null) {
sqlite3_log(-1, nativeString("dangling SQLite blob."));
closeNoCheck();
}
super.finalize();
}
/**
* Close this BLOB handle
* @return result code (No exception is thrown).
* sqlite3_blob_close
*/
public int closeNoCheck() {
if (pBlob == null) {
return SQLITE_OK;
}
final int res = sqlite3_blob_close(pBlob); // must be called only once
pBlob = null;
return res;
}
/**
* Close this BLOB and throw an exception if an error occured.
*/
@Override
public void close() throws SQLiteException {
final int res = closeNoCheck();
if (res != ErrCodes.SQLITE_OK) {
throw new SQLiteException(c, "error while closing Blob", res);
}
}
/**
* @return whether or not this BLOB is closed
*/
public boolean isClosed() {
return pBlob == null;
}
public void checkOpen() throws SQLiteException {
if (isClosed()) {
throw new SQLiteException(c, "blob already closed", ErrCodes.WRAPPER_SPECIFIC);
}
}
public OutputStream getOutputStream() {
return new BlobOutputStream();
}
public void setWriteOffset(int writeOffset) throws SQLiteException {
if (writeOffset < 0) {
throw new SQLiteException(String.format("invalid write offset: %d < 0", writeOffset), ErrCodes.WRAPPER_SPECIFIC);
} else if (writeOffset > getBytes()) {
throw new SQLiteException(String.format("invalid write offset: %d > %d", writeOffset, getBytes()), ErrCodes.WRAPPER_SPECIFIC);
}
this.writeOffset = writeOffset;
}
public InputStream getInputStream() {
return new BlobInputStream();
}
public void setReadOffset(int readOffset) throws SQLiteException {
if (readOffset < 0) {
throw new SQLiteException(String.format("invalid read offset: %d < 0", readOffset), ErrCodes.WRAPPER_SPECIFIC);
} else if (readOffset > getBytes()) {
throw new SQLiteException(String.format("invalid read offset: %d > %d", readOffset, getBytes()), ErrCodes.WRAPPER_SPECIFIC);
}
this.readOffset = readOffset;
}
private class BlobInputStream extends InputStream {
private int mark;
@Override
public int read() throws IOException {
if (isEOF()) {
return -1;
}
final byte[] b = new byte[1];
final int i = read(b);
if (i < 0) {
return i;
}
return b[0];
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (isEOF()) {
return -1;
}
final int avail = available();
if (len > avail) {
len = avail;
}
if (len <= 0) {
return 0;
}
try {
return Blob.this.read(ByteBuffer.wrap(b, off, len));
} catch (SQLiteException e) {
throw new IOException(e);
}
}
@Override
public long skip(long n) throws IOException {
try {
long k = getBytes() - readOffset;
if (n < k) {
k = n < 0L ? 0L : n;
}
readOffset += k;
return k;
} catch (SQLiteException e) {
throw new IOException(e);
}
}
@Override
public int available() throws IOException {
try {
return getBytes() - readOffset;
} catch (SQLiteException e) {
throw new IOException(e);
}
}
@Override
public void close() throws IOException {
try {
Blob.this.close();
} catch (SQLiteException e) {
throw new IOException(e);
}
}
private boolean isEOF() throws IOException {
try {
return readOffset >= getBytes();
} catch (SQLiteException e) {
throw new IOException(e);
}
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void mark(int unused) {
mark = readOffset;
}
@Override
public synchronized void reset() {
readOffset = mark;
}
}
private class BlobOutputStream extends OutputStream {
@Override
public void write(int b) throws IOException {
write(new byte[]{(byte) b});
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
try {
Blob.this.write(ByteBuffer.wrap(b, off, len));
} catch (SQLiteException e) {
throw new IOException(e);
}
}
@Override
public void close() throws IOException {
try {
Blob.this.close();
} catch (SQLiteException e) {
throw new IOException(e);
}
}
}
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
public static int copy(InputStream input, OutputStream output, int length) throws IOException {
final byte[] buffer = new byte[Math.min(length, DEFAULT_BUFFER_SIZE)];
int count = 0;
int n = buffer.length;
while (length > 0 && (n = input.read(buffer, 0, n)) >= 0) {
output.write(buffer, 0, n);
count += n;
length -= n;
n = Math.min(length, DEFAULT_BUFFER_SIZE);
}
return count;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy