com.helger.commons.io.stream.ByteBufferOutputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-commons Show documentation
Show all versions of ph-commons Show documentation
Java 1.8+ Library with tons of utility classes required in all projects
/*
* 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);
}
}