Обертывание ByteBuffer с InputStream - PullRequest
       21

Обертывание ByteBuffer с InputStream

39 голосов
/ 02 декабря 2010

У меня есть метод, который принимает InputStream и читает данные из него.Я хотел бы использовать этот метод также с ByteBuffer.Есть ли способ обернуть ByteBuffer, чтобы он мог быть доступен как поток?

Ответы [ 5 ]

65 голосов
/ 07 июля 2011

Кажется, есть некоторые ошибки в реализации, на которую ссылается Thilo, а также дословно копировать и вставлять на другие сайты:

  1. ByteBufferBackedInputStream.read() возвращает расширенный знак int, представляющий байт, который он читает, что неверно (значение должно быть в диапазоне [-1..255])
  2. ByteBufferBackedInputStream.read(byte[], int, int) не возвращает -1, если в буфере не осталось байтов, согласно спецификации API

ByteBufferBackedOutputStream кажется относительно надежным.

Ниже я представляю «исправленную» версию.Если я найду больше ошибок (или кто-то укажет на них), я обновлю их здесь.

Обновлено: удалено synchronized ключевых слов из методов чтения / записи

InputStream

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;
    }
}

OutputStream

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);
    }

}
13 голосов
/ 02 декабря 2010

Ничего в JDK, но есть множество реализаций, google для ByteBufferInputStream. По сути, они обертывают один или несколько байтовых буферов и отслеживают в них индекс, который записывает, сколько уже прочитано. Что-то , подобное этому , часто появляется, но, по-видимому, глючит, см. @ Mike Houston ответ на улучшенную версию ).

7 голосов
/ 02 декабря 2010

Если он поддерживается байтовым массивом, вы можете использовать ByteArrayInputStream и получить байтовый массив через ByteBuffer.array(). Это вызовет исключение, если вы попробуете это на нативном ByteBuffer.

2 голосов
/ 09 декабря 2015

Это моя версия реализации 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);
    }
}
1 голос
/ 29 сентября 2014

Используйте буфер кучи (байтовый массив) напрямую, если он доступен, в противном случае используйте обернутый байтовый буфер (см. Ответ Майка Хьюстона)

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);
}

Также обратите внимание, что обернутый буфер может эффективно поддерживать операции пометки / сброса и пропуска.

...