InputStreamを受け取り、そこからデータを読み取るメソッドがあります。このメソッドをByteBufferでも使用したいと思います。 ByteBufferをラップしてストリームとしてアクセスできるようにする方法はありますか?
JDKには何もありませんが、多くの実装があります。googleはByteBufferInputStreamです。基本的に、1つまたは複数のByteBufferをラップし、すでに読み取られた量を記録するインデックスを追跡します。このような何か がたくさん登場しますが 、明らかにバグがあります。 @ Mike Houstonの改善版についての回答を参照してください )。
Thiloが参照している実装にはいくつかのバグがあるようで、コピーして他のサイトにそのまま貼り付けています。
ByteBufferBackedInputStream.read()
は、読み取るバイトの符号拡張int表現を返しますが、これは間違っています(値は[-1..255]の範囲内でなければなりません)ByteBufferBackedInputStream.read(byte[], int, int)
は、API仕様に従って、バッファにバイトが残っていない場合、-1を返しません。ByteBufferBackedOutputStreamは比較的健全に見えます。
以下に「修正済み」バージョンを示します。さらにバグを見つけた場合(または誰かが指摘した場合)、ここで更新します。
更新:読み取り/書き込みメソッドからsynchronized
キーワードを削除
public class ByteBufferBackedInputStream extends InputStream {
ByteBuffer buf;
public ByteBufferBackedInputStream(ByteBuffer buf) {
this.buf = buf;
}
public int read() throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
return buf.get() & 0xFF;
}
public int read(byte[] bytes, int off, int len)
throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
len = Math.min(len, buf.remaining());
buf.get(bytes, off, len);
return len;
}
}
public class ByteBufferBackedOutputStream extends OutputStream {
ByteBuffer buf;
public ByteBufferBackedOutputStream(ByteBuffer buf) {
this.buf = buf;
}
public void write(int b) throws IOException {
buf.put((byte) b);
}
public void write(byte[] bytes, int off, int len)
throws IOException {
buf.put(bytes, off, len);
}
}
バイト配列によってサポートされている場合、ByteArrayInputStream
を使用して、ByteBuffer.array()
を介してバイト配列を取得できます。ネイティブのByteBufferで試行している場合、これは例外をスローします。
これは、私のInputStream
およびOutputStream
実装のバージョンです。
ByteBufferBackedInputStream
:
public class ByteBufferBackedInputStream extends InputStream
{
private ByteBuffer backendBuffer;
public ByteBufferBackedInputStream(ByteBuffer backendBuffer) {
Objects.requireNonNull(backendBuffer, "Given backend buffer can not be null!");
this.backendBuffer = backendBuffer;
}
public void close() throws IOException {
this.backendBuffer = null;
}
private void ensureStreamAvailable() throws IOException {
if (this.backendBuffer == null) {
throw new IOException("read on a closed InputStream!");
}
}
@Override
public int read() throws IOException {
this.ensureStreamAvailable();
return this.backendBuffer.hasRemaining() ? this.backendBuffer.get() & 0xFF : -1;
}
@Override
public int read(@Nonnull byte[] buffer) throws IOException {
return this.read(buffer, 0, buffer.length);
}
@Override
public int read(@Nonnull byte[] buffer, int offset, int length) throws IOException {
this.ensureStreamAvailable();
Objects.requireNonNull(buffer, "Given buffer can not be null!");
if (offset >= 0 && length >= 0 && length <= buffer.length - offset) {
if (length == 0) {
return 0;
}
else {
int remainingSize = Math.min(this.backendBuffer.remaining(), length);
if (remainingSize == 0) {
return -1;
}
else {
this.backendBuffer.get(buffer, offset, remainingSize);
return remainingSize;
}
}
}
else {
throw new IndexOutOfBoundsException();
}
}
public long skip(long n) throws IOException {
this.ensureStreamAvailable();
if (n <= 0L) {
return 0L;
}
int length = (int) n;
int remainingSize = Math.min(this.backendBuffer.remaining(), length);
this.backendBuffer.position(this.backendBuffer.position() + remainingSize);
return (long) length;
}
public int available() throws IOException {
this.ensureStreamAvailable();
return this.backendBuffer.remaining();
}
public synchronized void mark(int var1) {
}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public boolean markSupported() {
return false;
}
}
ByteBufferBackedOutputStream
:
public class ByteBufferBackedOutputStream extends OutputStream
{
private ByteBuffer backendBuffer;
public ByteBufferBackedOutputStream(ByteBuffer backendBuffer) {
Objects.requireNonNull(backendBuffer, "Given backend buffer can not be null!");
this.backendBuffer = backendBuffer;
}
public void close() throws IOException {
this.backendBuffer = null;
}
private void ensureStreamAvailable() throws IOException {
if (this.backendBuffer == null) {
throw new IOException("write on a closed OutputStream");
}
}
@Override
public void write(int b) throws IOException {
this.ensureStreamAvailable();
backendBuffer.put((byte) b);
}
@Override
public void write(@Nonnull byte[] bytes) throws IOException {
this.write(bytes, 0, bytes.length);
}
@Override
public void write(@Nonnull byte[] bytes, int off, int len) throws IOException {
this.ensureStreamAvailable();
Objects.requireNonNull(bytes, "Given buffer can not be null!");
if ((off < 0) || (off > bytes.length) || (len < 0) ||
((off + len) > bytes.length) || ((off + len) < 0))
{
throw new IndexOutOfBoundsException();
}
else if (len == 0) {
return;
}
backendBuffer.put(bytes, off, len);
}
}
可能であれば、ヒープバッファ(バイト配列)を直接使用します。それ以外の場合は、ラップされたバイトバッファを使用します(マイクヒューストンの回答を参照)
public static InputStream asInputStream(ByteBuffer buffer) {
if (buffer.hasArray()) {
// use heap buffer; no array is created; only the reference is used
return new ByteArrayInputStream(buffer.array());
}
return new ByteBufferInputStream(buffer);
}
また、ラップされたバッファは、マーク/リセットおよびスキップ操作を効率的にサポートできることに注意してください。