/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.io; import net.jcip.annotations.NotThreadSafe; import java.io.ByteArrayOutputStream; /** * Extends ByteArrayOutputStream, but exposes the internal buffer. * Using this, callers don't need to call toByteArray() which copies the * internal buffer. *
* Also overrides the superclass' behavior of always doubling the size of the * internal buffer any time more capacity is needed. This class doubles the * size until the internal buffer reaches a configurable max size (default is * 4MB), after which it begins growing the buffer in 25% increments. This is * intended to help prevent an OutOfMemoryError during a resize of a large * buffer. *
** A version of this class was originally created by Bela Ban as part of the * JGroups library. *
* This class is not threadsafe as it will not support concurrent readers and writers. * * * @author Brian Stansberry * */ @NotThreadSafe public class ExposedByteArrayOutputStream extends ByteArrayOutputStream { /** * Default buffer size after which if more buffer capacity * is needed the buffer will grow by 25% rather than 100% */ public static final int DEFAULT_DOUBLING_SIZE = 4 * 1024 * 1024; // 4MB private int maxDoublingSize = DEFAULT_DOUBLING_SIZE; public ExposedByteArrayOutputStream() { super(); } public ExposedByteArrayOutputStream(int size) { super(size); } /** * Creates a new byte array output stream, with a buffer capacity of * the specified size, in bytes. * * @param size the initial size. * @param maxDoublingSize the buffer size, after which if more capacity * is needed the buffer will grow by 25% * rather than 100% * @throws IllegalArgumentException if size is negative. */ public ExposedByteArrayOutputStream(int size, int maxDoublingSize) { super(size); this.maxDoublingSize = maxDoublingSize; } /** * Gets the internal buffer array. Note that the length of this array * will almost certainly be longer than the data written to it; call *size()
to get the number of bytes of actual data.
*/
public final byte[] getRawBuffer()
{
return buf;
}
@Override
public void write(byte[] b, int off, int len)
{
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0))
{
throw new IndexOutOfBoundsException();
}
else if (len == 0)
{
return;
}
int newcount = count + len;
if (newcount > buf.length)
{
byte newbuf[] = new byte[getNewBufferSize(buf.length, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
System.arraycopy(b, off, buf, count, len);
count = newcount;
}
@Override
public void write(int b)
{
int newcount = count + 1;
if (newcount > buf.length)
{
byte newbuf[] = new byte[getNewBufferSize(buf.length, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
buf[count] = (byte) b;
count = newcount;
}
/**
* Gets the highest internal buffer size after which if more capacity
* is needed the buffer will grow in 25% increments rather than 100%.
*/
public final int getMaxDoublingSize()
{
return maxDoublingSize;
}
/**
* Gets the number of bytes to which the internal buffer should be resized.
*
* @param curSize the current number of bytes
* @param minNewSize the minimum number of bytes required
* @return the size to which the internal buffer should be resized
*/
public final int getNewBufferSize(int curSize, int minNewSize)
{
if (curSize <= maxDoublingSize)
return Math.max(curSize << 1, minNewSize);
else
return Math.max(curSize + (curSize >> 2), minNewSize);
}
}