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

com.phloc.commons.io.streams.ByteBufferOutputStream Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
/**
 * Copyright (C) 2006-2015 phloc systems
 * http://www.phloc.com
 * office[at]phloc[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.phloc.commons.io.streams;

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.phloc.commons.ValueEnforcer;
import com.phloc.commons.annotations.ReturnsMutableCopy;
import com.phloc.commons.charset.CharsetManager;
import com.phloc.commons.collections.ArrayHelper;

/**
 * Wrapper for an {@link java.io.OutputStream} around a
 * {@link java.nio.ByteBuffer}.
 * 
 * @author Philip Helger
 */
public class ByteBufferOutputStream extends OutputStream
{
  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)
  {
    this.m_aBuffer = ValueEnforcer.notNull (aBuffer, "Buffer");
    this.m_bCanGrow = bCanGrow;
  }

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

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

  @Override
  public void close ()
  {}

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

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

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

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

  /**
   * 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)
  {
    ValueEnforcer.notNull (aDestBuffer, "DestBuffer");

    this.m_aBuffer.flip ();
    aDestBuffer.put (this.m_aBuffer);
    this.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);
  }

  /**
   * 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)
  {
    ValueEnforcer.isArrayOfsLen (aBuf, nOfs, nLen);

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

  /**
   * Write everything to the passed output stream and clear the contained
   * buffer.
   * 
   * @param aOS
   *        The output stream to write to. May not be null.
   * @throws IOException
   *         eventually
   */
  public void writeTo (@Nonnull @WillNotClose final OutputStream aOS) throws IOException
  {
    ValueEnforcer.notNull (aOS, "OutputStream");

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

  /**
   * Get the content as a string without modifying the buffer
   * 
   * @param aCharset
   *        The charset to use. May not be null.
   * @return The String representation.
   */
  @Nonnull
  public String getAsString (@Nonnull final Charset aCharset)
  {
    return CharsetManager.getAsString (this.m_aBuffer.array (),
                                       this.m_aBuffer.arrayOffset (),
                                       this.m_aBuffer.position (),
                                       aCharset);
  }

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

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

    this.m_aBuffer.put ((byte) b);
  }

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

    this.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 (this.m_bCanGrow && aSrcBuffer.remaining () > this.m_aBuffer.remaining ())
      _growBy (aSrcBuffer.remaining ());

    this.m_aBuffer.put (aSrcBuffer);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy