1c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta/*
2c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * Copyright (C) 2012 The Android Open Source Project
3c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta *
4c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * Licensed under the Apache License, Version 2.0 (the "License");
5c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * you may not use this file except in compliance with the License.
6c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * You may obtain a copy of the License at
7c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta *
8c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta *      http://www.apache.org/licenses/LICENSE-2.0
9c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta *
10c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * Unless required by applicable law or agreed to in writing, software
11c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * distributed under the License is distributed on an "AS IS" BASIS,
12c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * See the License for the specific language governing permissions and
14c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * limitations under the License.
15c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta */
16c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
17c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartapackage com.android.volley.toolbox;
18c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
19c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartaimport java.util.ArrayList;
20c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartaimport java.util.Collections;
21c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartaimport java.util.Comparator;
22c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartaimport java.util.LinkedList;
23c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartaimport java.util.List;
24c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
25c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta/**
26c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to
27c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * supply those buffers to consumers who need to use them for a short period of time and then
28c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * dispose of them. Simply creating and disposing such buffers in the conventional manner can
29c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * considerable heap churn and garbage collection delays on Android, which lacks good management of
30c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * short-lived heap objects. It may be advantageous to trade off some memory in the form of a
31c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * permanently allocated pool of buffers in order to gain heap performance improvements; that is
32c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * what this class does.
33c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * <p>
34c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * A good candidate user for this class is something like an I/O system that uses large temporary
35c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants
36c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks
37c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into
38c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * account and also to maximize the odds of being able to reuse a recycled buffer, this class is
39c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * free to return buffers larger than the requested size. The caller needs to be able to gracefully
40c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * deal with getting buffers any size over the minimum.
41c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * <p>
42c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
43c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * class will allocate a new buffer and return it.
44c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * <p>
45c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * This class has no special ownership of buffers it creates; the caller is free to take a buffer
46c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * it receives from this pool, use it permanently, and never return it to the pool; additionally,
47c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there
48c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * are no other lingering references to it.
49c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * <p>
50c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * This class ensures that the total size of the buffers in its recycling pool never exceeds a
51c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
52c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta * least-recently-used buffers are disposed.
53c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta */
54c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Bartapublic class ByteArrayPool {
55c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /** The buffer pool, arranged both by last use and by buffer size */
56c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
57c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
58c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
59c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /** The total size of the buffers in the pool */
60c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    private int mCurrentSize = 0;
61c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
62c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /**
63c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
64c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * under this limit.
65c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     */
66c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    private final int mSizeLimit;
67c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
68c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /** Compares buffers by size */
69c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {
70c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        @Override
71c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        public int compare(byte[] lhs, byte[] rhs) {
72c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            return lhs.length - rhs.length;
73c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        }
74c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    };
75c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
76c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /**
77c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * @param sizeLimit the maximum size of the pool, in bytes
78c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     */
79c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    public ByteArrayPool(int sizeLimit) {
80c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        mSizeLimit = sizeLimit;
81c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    }
82c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
83c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /**
84c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * Returns a buffer from the pool if one is available in the requested size, or allocates a new
85c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * one if a pooled one is not available.
86c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     *
87c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
88c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     *        larger.
89c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * @return a byte[] buffer is always returned.
90c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     */
91c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    public synchronized byte[] getBuf(int len) {
92c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        for (int i = 0; i < mBuffersBySize.size(); i++) {
93c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            byte[] buf = mBuffersBySize.get(i);
94c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            if (buf.length >= len) {
95c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta                mCurrentSize -= buf.length;
96c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta                mBuffersBySize.remove(i);
97c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta                mBuffersByLastUse.remove(buf);
98c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta                return buf;
99c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            }
100c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        }
101c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        return new byte[len];
102c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    }
103c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
104c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /**
105c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
106c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * size.
107c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     *
108c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * @param buf the buffer to return to the pool.
109c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     */
110c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    public synchronized void returnBuf(byte[] buf) {
111c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        if (buf == null || buf.length > mSizeLimit) {
112c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            return;
113c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        }
114c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        mBuffersByLastUse.add(buf);
115c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
116c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        if (pos < 0) {
117c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            pos = -pos - 1;
118c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        }
119c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        mBuffersBySize.add(pos, buf);
120c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        mCurrentSize += buf.length;
121c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        trim();
122c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    }
123c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
124c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    /**
125c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     * Removes buffers from the pool until it is under its size limit.
126c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta     */
127c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    private synchronized void trim() {
128c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        while (mCurrentSize > mSizeLimit) {
129c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            byte[] buf = mBuffersByLastUse.remove(0);
130c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            mBuffersBySize.remove(buf);
131c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta            mCurrentSize -= buf.length;
132c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta        }
133c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta    }
134c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta
135c4cbfcb8d044cea99e2471ce5c401cd959b6cdfeScott Barta}
136