1d5bf0ecf31f33fb31763995e3c8bfab69dc2457fWei-Ta Chen/*
258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * Copyright (C) 2012 The Android Open Source Project
358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot *
458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * Licensed under the Apache License, Version 2.0 (the "License");
558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * you may not use this file except in compliance with the License.
658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * You may obtain a copy of the License at
7d5bf0ecf31f33fb31763995e3c8bfab69dc2457fWei-Ta Chen *
858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot *      http://www.apache.org/licenses/LICENSE-2.0
958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot *
1058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * Unless required by applicable law or agreed to in writing, software
1158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * distributed under the License is distributed on an "AS IS" BASIS,
1258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * See the License for the specific language governing permissions and
146699e7ea2e981dccc2f3c41b5dcf1c860b11558dJean-Baptiste Queru * limitations under the License.
15a8dbe3f21c692b274486d03e7e44b4d19a0ea06bSkia_Android Canary Bot */
16a8dbe3f21c692b274486d03e7e44b4d19a0ea06bSkia_Android Canary Bot
17a8dbe3f21c692b274486d03e7e44b4d19a0ea06bSkia_Android Canary Botpackage com.android.volley.toolbox;
1858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
1958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Botimport java.util.ArrayList;
2058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Botimport java.util.Collections;
2158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Botimport java.util.Comparator;
2258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Botimport java.util.LinkedList;
2358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Botimport java.util.List;
2458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
2558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot/**
2658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to
2758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * supply those buffers to consumers who need to use them for a short period of time and then
2858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * dispose of them. Simply creating and disposing such buffers in the conventional manner can
2958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * considerable heap churn and garbage collection delays on Android, which lacks good management of
3058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * short-lived heap objects. It may be advantageous to trade off some memory in the form of a
3158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * permanently allocated pool of buffers in order to gain heap performance improvements; that is
3258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * what this class does.
3358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * <p>
3458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * A good candidate user for this class is something like an I/O system that uses large temporary
3558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants
3658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks
3758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into
3858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * account and also to maximize the odds of being able to reuse a recycled buffer, this class is
3958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * free to return buffers larger than the requested size. The caller needs to be able to gracefully
4058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * deal with getting buffers any size over the minimum.
4158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * <p>
4258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
4358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * class will allocate a new buffer and return it.
4458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * <p>
4558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * This class has no special ownership of buffers it creates; the caller is free to take a buffer
4658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * it receives from this pool, use it permanently, and never return it to the pool; additionally,
4758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there
4858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * are no other lingering references to it.
4958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * <p>
5058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * This class ensures that the total size of the buffers in its recycling pool never exceeds a
5158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
5258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot * least-recently-used buffers are disposed.
5358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot */
5458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Botpublic class ByteArrayPool {
5558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /** The buffer pool, arranged both by last use and by buffer size */
5658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
5758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
5858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
5958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /** The total size of the buffers in the pool */
6058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    private int mCurrentSize = 0;
6158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
6258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /**
6358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
6458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * under this limit.
6558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     */
6658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    private final int mSizeLimit;
6758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
6858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /** Compares buffers by size */
6958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {
7058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        @Override
7158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        public int compare(byte[] lhs, byte[] rhs) {
7258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            return lhs.length - rhs.length;
7358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        }
7458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    };
7558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
7658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /**
7758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * @param sizeLimit the maximum size of the pool, in bytes
7858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     */
7958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    public ByteArrayPool(int sizeLimit) {
8058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        mSizeLimit = sizeLimit;
8158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    }
8258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
8358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /**
8458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * Returns a buffer from the pool if one is available in the requested size, or allocates a new
8558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * one if a pooled one is not available.
8658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     *
8758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
8858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     *        larger.
8958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * @return a byte[] buffer is always returned.
9058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     */
9158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    public synchronized byte[] getBuf(int len) {
9258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        for (int i = 0; i < mBuffersBySize.size(); i++) {
9358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            byte[] buf = mBuffersBySize.get(i);
9458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            if (buf.length >= len) {
9558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot                mCurrentSize -= buf.length;
9658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot                mBuffersBySize.remove(i);
9758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot                mBuffersByLastUse.remove(buf);
9858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot                return buf;
9958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            }
10058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        }
10158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        return new byte[len];
10258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    }
10358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
10458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /**
10558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
10658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * size.
107f56b9c160fe9eba8bfa1779a4ee54b0082a278b3Skia_Android Canary Bot     *
10858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * @param buf the buffer to return to the pool.
10958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     */
11058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    public synchronized void returnBuf(byte[] buf) {
11158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        if (buf == null || buf.length > mSizeLimit) {
11258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            return;
11358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        }
11458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        mBuffersByLastUse.add(buf);
11558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
11658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        if (pos < 0) {
117dd9fdd91285c6e71431bafc11d0d7b5bcabb203fSkia_Android Canary Bot            pos = -pos - 1;
11858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        }
11958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        mBuffersBySize.add(pos, buf);
12058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        mCurrentSize += buf.length;
12158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        trim();
12258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    }
12358b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
12458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    /**
12558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     * Removes buffers from the pool until it is under its size limit.
12658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot     */
12758b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot    private synchronized void trim() {
12858b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        while (mCurrentSize > mSizeLimit) {
12958b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            byte[] buf = mBuffersByLastUse.remove(0);
13058b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            mBuffersBySize.remove(buf);
13158b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot            mCurrentSize -= buf.length;
13258b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot        }
1339e143897ca864c4b577887b26250584ad1e2a75fSkia_Android Canary Bot    }
13458b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot
13558b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot}
13658b38c2d473883bbc7f7c8a080560fe117cdfef6Skia_Android Canary Bot