/* * Copyright (C) 2014 Neal * * 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 neal.http.utils; /** * Created by Neal on 2014/10/28. */ import java.io.ByteArrayOutputStream; import java.io.IOException; /** * A variation of {@link java.io.ByteArrayOutputStream} that uses a pool of byte[] buffers instead * of always allocating them fresh, saving on heap churn. */ public class PoolingByteArrayOutputStream extends ByteArrayOutputStream { /** * If the {@link #PoolingByteArrayOutputStream(neal.http.utils.ByteArrayPool)} constructor is called, this is * the default size to which the underlying byte array is initialized. */ private static final int DEFAULT_SIZE = 256; private final ByteArrayPool mPool; /** * Constructs a new PoolingByteArrayOutputStream with a default size. If more bytes are written * to this instance, the underlying byte array will expand. */ public PoolingByteArrayOutputStream(ByteArrayPool pool) { this(pool, DEFAULT_SIZE); } /** * Constructs a new {@code ByteArrayOutputStream} with a default size of {@code size} bytes. If * more than {@code size} bytes are written to this instance, the underlying byte array will * expand. * * @param size initial size for the underlying byte array. The value will be pinned to a default * minimum size. */ public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) { mPool = pool; buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE)); } @Override public void close() throws IOException { mPool.returnBuf(buf); buf = null; super.close(); } @Override public void finalize() { mPool.returnBuf(buf); } /** * Ensures there is enough space in the buffer for the given number of additional bytes. */ private void expand(int i) { /* Can the buffer handle @i more bytes, if not expand it */ if (count + i <= buf.length) { return; } byte[] newbuf = mPool.getBuf((count + i) * 2); System.arraycopy(buf, 0, newbuf, 0, count); mPool.returnBuf(buf); buf = newbuf; } @Override public synchronized void write(byte[] buffer, int offset, int len) { expand(len); super.write(buffer, offset, len); } @Override public synchronized void write(int oneByte) { expand(1); super.write(oneByte); } }