• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ByteArrayBuilder()42     public ByteArrayBuilder() {
43         mChunks = new LinkedList<Chunk>();
44     }
45 
append(byte[] array, int offset, int length)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      */
getFirstChunk()73     public synchronized Chunk getFirstChunk() {
74         if (mChunks.isEmpty()) return null;
75         return mChunks.removeFirst();
76     }
77 
isEmpty()78     public synchronized boolean isEmpty() {
79         return mChunks.isEmpty();
80     }
81 
getByteSize()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 
clear()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.
processPoolLocked()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 
obtainChunk(int length)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 
Chunk(int length)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          */
release()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