content: /*
   * Copyright (C) 2012 The Android Open Source Project
   *
   * 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 com.cjj.volley.toolbox;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.LinkedList;
  import java.util.List;
  
  /**
   * ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to
   * supply those buffers to consumers who need to use them for a short period of time and then
   * dispose of them. Simply creating and disposing such buffers in the conventional manner can
   * considerable heap churn and garbage collection delays on Android, which lacks good management of
   * short-lived heap objects. It may be advantageous to trade off some memory in the form of a
   * permanently allocated pool of buffers in order to gain heap performance improvements; that is
   * what this class does.
   * <p>
   * A good candidate user for this class is something like an I/O system that uses large temporary
   * <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants
   * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks
   * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into
   * account and also to maximize the odds of being able to reuse a recycled buffer, this class is
   * free to return buffers larger than the requested size. The caller needs to be able to gracefully
   * deal with getting buffers any size over the minimum.
   * <p>
   * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
   * class will allocate a new buffer and return it.
   * <p>
   * This class has no special ownership of buffers it creates; the caller is free to take a buffer
   * it receives from this pool, use it permanently, and never return it to the pool; additionally,
   * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there
   * are no other lingering references to it.
   * <p>
   * This class ensures that the total size of the buffers in its recycling pool never exceeds a
   * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
   * least-recently-used buffers are disposed.
   */
  public class ByteArrayPool {
      /** The buffer pool, arranged both by last use and by buffer size */
      private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
      private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
  
      /** The total size of the buffers in the pool */
      private int mCurrentSize = 0;
  
      /**
       * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
       * under this limit.
       */
      private final int mSizeLimit;
  
      /** Compares buffers by size */
      protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {
          @Override
          public int compare(byte[] lhs, byte[] rhs) {
              return lhs.length - rhs.length;
          }
      };
  
      /**
       * @param sizeLimit the maximum size of the pool, in bytes
       */
      public ByteArrayPool(int sizeLimit) {
          mSizeLimit = sizeLimit;
      }
  
      /**
       * Returns a buffer from the pool if one is available in the requested size, or allocates a new
       * one if a pooled one is not available.
       *
       * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
       *        larger.
       * @return a byte[] buffer is always returned.
       */
      public synchronized byte[] getBuf(int len) {
          for (int i = 0; i < mBuffersBySize.size(); i++) {
              byte[] buf = mBuffersBySize.get(i);
              if (buf.length >= len) {
                  mCurrentSize -= buf.length;
                  mBuffersBySize.remove(i);
                  mBuffersByLastUse.remove(buf);
                  return buf;
              }
          }
          return new byte[len];
      }
  
      /**
       * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
       * size.
       *
       * @param buf the buffer to return to the pool.
       */
      public synchronized void returnBuf(byte[] buf) {
          if (buf == null || buf.length > mSizeLimit) {
              return;
          }
          mBuffersByLastUse.add(buf);
          int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
          if (pos < 0) {
              pos = -pos - 1;
          }
          mBuffersBySize.add(pos, buf);
          mCurrentSize += buf.length;
          trim();
      }
  
      /**
       * Removes buffers from the pool until it is under its size limit.
       */
      private synchronized void trim() {
          while (mCurrentSize > mSizeLimit) {
              byte[] buf = mBuffersByLastUse.remove(0);
              mBuffersBySize.remove(buf);
              mCurrentSize -= buf.length;
          }
      }
  
  }
  