All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.commons.io.stream.ByteBufferOutputStream Maven / Gradle / Ivy

There is a newer version: 11.1.10
Show newest version
/*
 * Copyright (C) 2014-2024 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.helger.commons.io.stream;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.WillNotClose;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.collection.ArrayHelper;
import com.helger.commons.io.ByteArrayWrapper;
import com.helger.commons.io.IWriteToStream;

/**
 * Wrapper for an {@link java.io.OutputStream} around a
 * {@link java.nio.ByteBuffer}.
 *
 * @author Philip Helger
 */
public class ByteBufferOutputStream extends OutputStream implements IWriteToStream
{
  public static final int DEFAULT_BUF_SIZE = 1024;
  public static final boolean DEFAULT_CAN_GROW = true;

  private ByteBuffer m_aBuffer;
  private final boolean m_bCanGrow;

  /**
   * Create a new object with the {@link #DEFAULT_BUF_SIZE} buffer size and it
   * can grow.
   */
  public ByteBufferOutputStream ()
  {
    this (DEFAULT_BUF_SIZE, DEFAULT_CAN_GROW);
  }

  /**
   * Constructor for an output stream than can grow.
   *
   * @param nBytes
   *        The initial number of bytes the buffer has. Must be ≥ 0.
   */
  public ByteBufferOutputStream (@Nonnegative final int nBytes)
  {
    this (nBytes, DEFAULT_CAN_GROW);
  }

  /**
   * Constructor
   *
   * @param nBytes
   *        The number of bytes the buffer has initially. Must be ≥ 0.
   * @param bCanGrow
   *        true if the buffer can grow, false
   *        otherwise.
   */
  public ByteBufferOutputStream (@Nonnegative final int nBytes, final boolean bCanGrow)
  {
    this (ByteBuffer.allocate (nBytes), bCanGrow);
  }

  /**
   * Constructor with an existing byte array to wrap. This output stream cannot
   * grow!
   *
   * @param aArray
   *        The array to be backed by a {@link ByteBuffer}.
   */
  public ByteBufferOutputStream (@Nonnull final byte [] aArray)
  {
    this (ByteBuffer.wrap (aArray), false);
  }

  /**
   * Constructor with an existing byte array to wrap. This output stream cannot
   * grow!
   *
   * @param aArray
   *        The array to be backed by a {@link ByteBuffer}.
   * @param nOfs
   *        Offset into the byte array. Must be ≥ 0.
   * @param nLen
   *        Number of bytes to wrap. Must be ≥ 0.
   */
  public ByteBufferOutputStream (@Nonnull final byte [] aArray, @Nonnegative final int nOfs, @Nonnegative final int nLen)
  {
    this (ByteBuffer.wrap (aArray, nOfs, nLen), false);
  }

  /**
   * Constructor
   *
   * @param aBuffer
   *        The byte buffer to use. May not be null.
   * @param bCanGrow
   *        true if the buffer can grow, false
   *        otherwise.
   */
  public ByteBufferOutputStream (@Nonnull final ByteBuffer aBuffer, final boolean bCanGrow)
  {
    ValueEnforcer.notNull (aBuffer, "Buffer");

    m_aBuffer = aBuffer;
    m_bCanGrow = bCanGrow;
  }

  /**
   * @return The contained buffer. Never null.
   */
  @Nonnull
  public ByteBuffer getBuffer ()
  {
    return m_aBuffer;
  }

  /**
   * @return true if this buffer can grow, false if
   *         not.
   */
  public boolean canGrow ()
  {
    return m_bCanGrow;
  }

  @Override
  public void close ()
  {}

  /**
   * Reset the backing byte buffer
   */
  public void reset ()
  {
    m_aBuffer.clear ();
  }

  /**
   * @return The number of bytes currently in the buffer. Always ≥ 0.
   */
  @Nonnegative
  public int size ()
  {
    return m_aBuffer.position ();
  }

  /**
   * Get everything as a big byte array, without altering the ByteBuffer. This
   * works only if the contained ByteBuffer has a backing array.
   *
   * @return The content of the buffer as a byte array. Never null.
   */
  @Nonnull
  @ReturnsMutableCopy
  public byte [] getAsByteArray ()
  {
    final byte [] aArray = m_aBuffer.array ();
    final int nOfs = m_aBuffer.arrayOffset ();
    final int nLength = m_aBuffer.position ();

    return ArrayHelper.getCopy (aArray, nOfs, nLength);
  }

  @Nonnull
  public NonBlockingByteArrayInputStream getAsByteArrayInputStream (final boolean bCopyNeeded)
  {
    final byte [] aBytes = m_aBuffer.array ();
    final int nOfs = m_aBuffer.arrayOffset ();
    final int nLength = m_aBuffer.position ();

    return new NonBlockingByteArrayInputStream (aBytes, nOfs, nLength, bCopyNeeded);
  }

  @Nonnull
  public ByteArrayWrapper getAsByteArrayWrapper (final boolean bCopyNeeded)
  {
    final byte [] aBytes = m_aBuffer.array ();
    final int nOfs = m_aBuffer.arrayOffset ();
    final int nLength = m_aBuffer.position ();

    return new ByteArrayWrapper (aBytes, nOfs, nLength, bCopyNeeded);
  }

  /**
   * Write everything currently contained to the specified buffer. If the passed
   * buffer is too small, a {@link java.nio.BufferOverflowException} is thrown.
   * The copied elements are removed from this streams buffer.
   *
   * @param aDestBuffer
   *        The destination buffer to write to. May not be null.
   */
  public void writeTo (@Nonnull final ByteBuffer aDestBuffer)
  {
    writeTo (aDestBuffer, true);
  }

  /**
   * Write everything currently contained to the specified buffer. If the passed
   * buffer is too small, a {@link java.nio.BufferOverflowException} is thrown.
   * The copied elements are removed from this streams buffer.
   *
   * @param aDestBuffer
   *        The destination buffer to write to. May not be null.
   * @param bCompactBuffer
   *        true to compact the buffer afterwards,
   *        false otherwise.
   */
  public void writeTo (@Nonnull final ByteBuffer aDestBuffer, final boolean bCompactBuffer)
  {
    ValueEnforcer.notNull (aDestBuffer, "DestBuffer");

    m_aBuffer.flip ();
    aDestBuffer.put (m_aBuffer);
    if (bCompactBuffer)
      m_aBuffer.compact ();
  }

  /**
   * Writes the current content to the passed buffer. The copied elements are
   * removed from this streams buffer.
   *
   * @param aBuf
   *        The buffer to be filled. May not be null.
   */
  public void writeTo (@Nonnull final byte [] aBuf)
  {
    ValueEnforcer.notNull (aBuf, "Buffer");

    writeTo (aBuf, 0, aBuf.length, true);
  }

  /**
   * Writes the current content to the passed buffer. The copied elements are
   * removed from this streams buffer.
   *
   * @param aBuf
   *        The buffer to be filled. May not be null.
   * @param bCompactBuffer
   *        true to compact the buffer afterwards,
   *        false otherwise.
   */
  public void writeTo (@Nonnull final byte [] aBuf, final boolean bCompactBuffer)
  {
    ValueEnforcer.notNull (aBuf, "Buffer");

    writeTo (aBuf, 0, aBuf.length, bCompactBuffer);
  }

  /**
   * Write current content to the passed byte array. The copied elements are
   * removed from this streams buffer.
   *
   * @param aBuf
   *        Byte array to write to. May not be null.
   * @param nOfs
   *        Offset to start writing. Must be ≥ 0.
   * @param nLen
   *        Number of bytes to copy. Must be ≥ 0.
   */
  public void writeTo (@Nonnull final byte [] aBuf, @Nonnegative final int nOfs, @Nonnegative final int nLen)
  {
    writeTo (aBuf, nOfs, nLen, true);
  }

  /**
   * Write current content to the passed byte array. The copied elements are
   * removed from this streams buffer.
   *
   * @param aBuf
   *        Byte array to write to. May not be null.
   * @param nOfs
   *        Offset to start writing. Must be ≥ 0.
   * @param nLen
   *        Number of bytes to copy. Must be ≥ 0.
   * @param bCompactBuffer
   *        true to compact the buffer afterwards,
   *        false otherwise.
   */
  public void writeTo (@Nonnull final byte [] aBuf, @Nonnegative final int nOfs, @Nonnegative final int nLen, final boolean bCompactBuffer)
  {
    ValueEnforcer.isArrayOfsLen (aBuf, nOfs, nLen);

    m_aBuffer.flip ();
    m_aBuffer.get (aBuf, nOfs, nLen);
    if (bCompactBuffer)
      m_aBuffer.compact ();
  }

  /**
   * Write everything to the passed output stream and clear the contained
   * buffer. This works only if the contained ByteBuffer has a backing array.
   *
   * @param aOS
   *        The output stream to write to. May not be null.
   * @throws IOException
   *         In case of IO error
   */
  public void writeTo (@Nonnull @WillNotClose final OutputStream aOS) throws IOException
  {
    writeTo (aOS, true);
  }

  /**
   * Write everything to the passed output stream and optionally clear the
   * contained buffer. This works only if the contained ByteBuffer has a backing
   * array.
   *
   * @param aOS
   *        The output stream to write to. May not be null.
   * @param bClearBuffer
   *        true to clear the buffer, false to not do
   *        it. If false you may call {@link #reset()} to clear it
   *        manually afterwards.
   * @throws IOException
   *         In case of IO error
   */
  public void writeTo (@Nonnull @WillNotClose final OutputStream aOS, final boolean bClearBuffer) throws IOException
  {
    ValueEnforcer.notNull (aOS, "OutputStream");

    aOS.write (m_aBuffer.array (), m_aBuffer.arrayOffset (), m_aBuffer.position ());
    if (bClearBuffer)
      m_aBuffer.clear ();
  }

  /**
   * Get the content as a string without modifying the buffer. This works only
   * if the contained ByteBuffer has a backing array.
   *
   * @param aCharset
   *        The charset to use. May not be null.
   * @return The String representation.
   */
  @Nonnull
  public String getAsString (@Nonnull final Charset aCharset)
  {
    return new String (m_aBuffer.array (), m_aBuffer.arrayOffset (), m_aBuffer.position (), aCharset);
  }

  private void _growBy (@Nonnegative final int nBytesToGrow)
  {
    final int nCurSize = m_aBuffer.capacity ();
    final int nNewSize = Math.max (nCurSize << 1, nCurSize + nBytesToGrow);
    final ByteBuffer aNewBuffer = ByteBuffer.allocate (nNewSize);
    m_aBuffer.flip ();
    aNewBuffer.put (m_aBuffer);
    m_aBuffer = aNewBuffer;
  }

  @Override
  public void write (final int b)
  {
    if (m_bCanGrow && !m_aBuffer.hasRemaining ())
      _growBy (1);

    m_aBuffer.put ((byte) b);
  }

  @Override
  public void write (@Nonnull final byte [] aBuf, @Nonnegative final int nOfs, @Nonnegative final int nLen)
  {
    if (m_bCanGrow && nLen > m_aBuffer.remaining ())
      _growBy (nLen);

    m_aBuffer.put (aBuf, nOfs, nLen);
  }

  /**
   * Write the content from the passed byte buffer to this output stream.
   *
   * @param aSrcBuffer
   *        The buffer to use. May not be null.
   */
  public void write (@Nonnull final ByteBuffer aSrcBuffer)
  {
    ValueEnforcer.notNull (aSrcBuffer, "SourceBuffer");

    if (m_bCanGrow && aSrcBuffer.remaining () > m_aBuffer.remaining ())
      _growBy (aSrcBuffer.remaining ());

    m_aBuffer.put (aSrcBuffer);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy