Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2006 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 android.webkit;
     18 
     19 import java.lang.ref.ReferenceQueue;
     20 import java.lang.ref.SoftReference;
     21 import java.util.LinkedList;
     22 import java.util.ListIterator;
     23 
     24 /** Utility class optimized for accumulating bytes, and then spitting
     25     them back out.  It does not optimize for returning the result in a
     26     single array, though this is supported in the API. It is fastest
     27     if the retrieval can be done via iterating through chunks.
     28 */
     29 class ByteArrayBuilder {
     30 
     31     private static final int DEFAULT_CAPACITY = 8192;
     32 
     33     // Global pool of chunks to be used by other ByteArrayBuilders.
     34     private static final LinkedList<SoftReference<Chunk>> sPool =
     35             new LinkedList<SoftReference<Chunk>>();
     36     // Reference queue for processing gc'd entries.
     37     private static final ReferenceQueue<Chunk> sQueue =
     38             new ReferenceQueue<Chunk>();
     39 
     40     private LinkedList<Chunk> mChunks;
     41 
     42     public ByteArrayBuilder() {
     43         mChunks = new LinkedList<Chunk>();
     44     }
     45 
     46     public synchronized void append(byte[] array, int offset, int length) {
     47         while (length > 0) {
     48             Chunk c = null;
     49             if (mChunks.isEmpty()) {
     50                 c = obtainChunk(length);
     51                 mChunks.addLast(c);
     52             } else {
     53                 c = mChunks.getLast();
     54                 if (c.mLength == c.mArray.length) {
     55                     c = obtainChunk(length);
     56                     mChunks.addLast(c);
     57                 }
     58             }
     59             int amount = Math.min(length, c.mArray.length - c.mLength);
     60             System.arraycopy(array, offset, c.mArray, c.mLength, amount);
     61             c.mLength += amount;
     62             length -= amount;
     63             offset += amount;
     64         }
     65     }
     66 
     67     /**
     68      * The fastest way to retrieve the data is to iterate through the
     69      * chunks.  This returns the first chunk.  Note: this pulls the
     70      * chunk out of the queue.  The caller must call Chunk.release() to
     71      * dispose of it.
     72      */
     73     public synchronized Chunk getFirstChunk() {
     74         if (mChunks.isEmpty()) return null;
     75         return mChunks.removeFirst();
     76     }
     77 
     78     public synchronized boolean isEmpty() {
     79         return mChunks.isEmpty();
     80     }
     81 
     82     public synchronized int getByteSize() {
     83         int total = 0;
     84         ListIterator<Chunk> it = mChunks.listIterator(0);
     85         while (it.hasNext()) {
     86             Chunk c = it.next();
     87             total += c.mLength;
     88         }
     89         return total;
     90     }
     91 
     92     public synchronized void clear() {
     93         Chunk c = getFirstChunk();
     94         while (c != null) {
     95             c.release();
     96             c = getFirstChunk();
     97         }
     98     }
     99 
    100     // Must be called with lock held on sPool.
    101     private void processPoolLocked() {
    102         while (true) {
    103             SoftReference<Chunk> entry = (SoftReference<Chunk>) sQueue.poll();
    104             if (entry == null) {
    105                 break;
    106             }
    107             sPool.remove(entry);
    108         }
    109     }
    110 
    111     private Chunk obtainChunk(int length) {
    112         // Correct a small length.
    113         if (length < DEFAULT_CAPACITY) {
    114             length = DEFAULT_CAPACITY;
    115         }
    116         synchronized (sPool) {
    117             // Process any queued references and remove them from the pool.
    118             processPoolLocked();
    119             if (!sPool.isEmpty()) {
    120                 Chunk c = sPool.removeFirst().get();
    121                 // The first item may have been queued after processPoolLocked
    122                 // so check for null.
    123                 if (c != null) {
    124                     return c;
    125                 }
    126             }
    127             return new Chunk(length);
    128         }
    129     }
    130 
    131     public static class Chunk {
    132         public byte[]  mArray;
    133         public int     mLength;
    134 
    135         public Chunk(int length) {
    136             mArray = new byte[length];
    137             mLength = 0;
    138         }
    139 
    140         /**
    141          * Release the chunk and make it available for reuse.
    142          */
    143         public void release() {
    144             mLength = 0;
    145             synchronized (sPool) {
    146                 // Add the chunk back to the pool as a SoftReference so it can
    147                 // be gc'd if needed.
    148                 sPool.offer(new SoftReference<Chunk>(this, sQueue));
    149                 sPool.notifyAll();
    150             }
    151         }
    152 
    153     }
    154 }
    155