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