1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.volley.toolbox;
18
19import java.io.ByteArrayOutputStream;
20import java.io.IOException;
21
22/**
23 * A variation of {@link java.io.ByteArrayOutputStream} that uses a pool of byte[] buffers instead
24 * of always allocating them fresh, saving on heap churn.
25 */
26public class PoolingByteArrayOutputStream extends ByteArrayOutputStream {
27    /**
28     * If the {@link #PoolingByteArrayOutputStream(ByteArrayPool)} constructor is called, this is
29     * the default size to which the underlying byte array is initialized.
30     */
31    private static final int DEFAULT_SIZE = 256;
32
33    private final ByteArrayPool mPool;
34
35    /**
36     * Constructs a new PoolingByteArrayOutputStream with a default size. If more bytes are written
37     * to this instance, the underlying byte array will expand.
38     */
39    public PoolingByteArrayOutputStream(ByteArrayPool pool) {
40        this(pool, DEFAULT_SIZE);
41    }
42
43    /**
44     * Constructs a new {@code ByteArrayOutputStream} with a default size of {@code size} bytes. If
45     * more than {@code size} bytes are written to this instance, the underlying byte array will
46     * expand.
47     *
48     * @param size initial size for the underlying byte array. The value will be pinned to a default
49     *        minimum size.
50     */
51    public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) {
52        mPool = pool;
53        buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE));
54    }
55
56    @Override
57    public void close() throws IOException {
58        mPool.returnBuf(buf);
59        buf = null;
60        super.close();
61    }
62
63    @Override
64    public void finalize() {
65        mPool.returnBuf(buf);
66    }
67
68    /**
69     * Ensures there is enough space in the buffer for the given number of additional bytes.
70     */
71    private void expand(int i) {
72        /* Can the buffer handle @i more bytes, if not expand it */
73        if (count + i <= buf.length) {
74            return;
75        }
76        byte[] newbuf = mPool.getBuf((count + i) * 2);
77        System.arraycopy(buf, 0, newbuf, 0, count);
78        mPool.returnBuf(buf);
79        buf = newbuf;
80    }
81
82    @Override
83    public synchronized void write(byte[] buffer, int offset, int len) {
84        expand(len);
85        super.write(buffer, offset, len);
86    }
87
88    @Override
89    public synchronized void write(int oneByte) {
90        expand(1);
91        super.write(oneByte);
92    }
93}
94