am e1f43dad: Share chunks between all ByteArrayBuilders.
Merge commit 'e1f43dadb9a1f0744f7e24373ca796c7a8ca2b12' into eclair-mr2-plus-aosp * commit 'e1f43dadb9a1f0744f7e24373ca796c7a8ca2b12': Share chunks between all ByteArrayBuilders.
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package android.webkit;
|
package android.webkit;
|
||||||
|
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
|
||||||
@@ -23,47 +25,37 @@ import java.util.ListIterator;
|
|||||||
them back out. It does not optimize for returning the result in a
|
them back out. It does not optimize for returning the result in a
|
||||||
single array, though this is supported in the API. It is fastest
|
single array, though this is supported in the API. It is fastest
|
||||||
if the retrieval can be done via iterating through chunks.
|
if the retrieval can be done via iterating through chunks.
|
||||||
|
|
||||||
Things to add:
|
|
||||||
- consider dynamically increasing our min_capacity,
|
|
||||||
as we see mTotalSize increase
|
|
||||||
*/
|
*/
|
||||||
class ByteArrayBuilder {
|
class ByteArrayBuilder {
|
||||||
|
|
||||||
private static final int DEFAULT_CAPACITY = 8192;
|
private static final int DEFAULT_CAPACITY = 8192;
|
||||||
|
|
||||||
|
// Global pool of chunks to be used by other ByteArrayBuilders.
|
||||||
|
private static final LinkedList<SoftReference<Chunk>> sPool =
|
||||||
|
new LinkedList<SoftReference<Chunk>>();
|
||||||
|
// Reference queue for processing gc'd entries.
|
||||||
|
private static final ReferenceQueue<Chunk> sQueue =
|
||||||
|
new ReferenceQueue<Chunk>();
|
||||||
|
|
||||||
private LinkedList<Chunk> mChunks;
|
private LinkedList<Chunk> mChunks;
|
||||||
|
|
||||||
/** free pool */
|
|
||||||
private LinkedList<Chunk> mPool;
|
|
||||||
|
|
||||||
private int mMinCapacity;
|
|
||||||
|
|
||||||
public ByteArrayBuilder() {
|
public ByteArrayBuilder() {
|
||||||
init(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteArrayBuilder(int minCapacity) {
|
|
||||||
init(minCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(int minCapacity) {
|
|
||||||
mChunks = new LinkedList<Chunk>();
|
mChunks = new LinkedList<Chunk>();
|
||||||
mPool = new LinkedList<Chunk>();
|
|
||||||
|
|
||||||
if (minCapacity <= 0) {
|
|
||||||
minCapacity = DEFAULT_CAPACITY;
|
|
||||||
}
|
|
||||||
mMinCapacity = minCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void append(byte[] array) {
|
|
||||||
append(array, 0, array.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void append(byte[] array, int offset, int length) {
|
public synchronized void append(byte[] array, int offset, int length) {
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
Chunk c = appendChunk(length);
|
Chunk c = null;
|
||||||
|
if (mChunks.isEmpty()) {
|
||||||
|
c = obtainChunk(length);
|
||||||
|
mChunks.addLast(c);
|
||||||
|
} else {
|
||||||
|
c = mChunks.getLast();
|
||||||
|
if (c.mLength == c.mArray.length) {
|
||||||
|
c = obtainChunk(length);
|
||||||
|
mChunks.addLast(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
int amount = Math.min(length, c.mArray.length - c.mLength);
|
int amount = Math.min(length, c.mArray.length - c.mLength);
|
||||||
System.arraycopy(array, offset, c.mArray, c.mLength, amount);
|
System.arraycopy(array, offset, c.mArray, c.mLength, amount);
|
||||||
c.mLength += amount;
|
c.mLength += amount;
|
||||||
@@ -75,7 +67,7 @@ class ByteArrayBuilder {
|
|||||||
/**
|
/**
|
||||||
* The fastest way to retrieve the data is to iterate through the
|
* The fastest way to retrieve the data is to iterate through the
|
||||||
* chunks. This returns the first chunk. Note: this pulls the
|
* chunks. This returns the first chunk. Note: this pulls the
|
||||||
* chunk out of the queue. The caller must call releaseChunk() to
|
* chunk out of the queue. The caller must call Chunk.release() to
|
||||||
* dispose of it.
|
* dispose of it.
|
||||||
*/
|
*/
|
||||||
public synchronized Chunk getFirstChunk() {
|
public synchronized Chunk getFirstChunk() {
|
||||||
@@ -83,23 +75,11 @@ class ByteArrayBuilder {
|
|||||||
return mChunks.removeFirst();
|
return mChunks.removeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public synchronized boolean isEmpty() {
|
||||||
* recycles chunk
|
|
||||||
*/
|
|
||||||
public synchronized void releaseChunk(Chunk c) {
|
|
||||||
c.mLength = 0;
|
|
||||||
mPool.addLast(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return mChunks.isEmpty();
|
return mChunks.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public synchronized int getByteSize() {
|
||||||
return mChunks.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getByteSize() {
|
|
||||||
int total = 0;
|
int total = 0;
|
||||||
ListIterator<Chunk> it = mChunks.listIterator(0);
|
ListIterator<Chunk> it = mChunks.listIterator(0);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
@@ -112,37 +92,37 @@ class ByteArrayBuilder {
|
|||||||
public synchronized void clear() {
|
public synchronized void clear() {
|
||||||
Chunk c = getFirstChunk();
|
Chunk c = getFirstChunk();
|
||||||
while (c != null) {
|
while (c != null) {
|
||||||
releaseChunk(c);
|
c.release();
|
||||||
c = getFirstChunk();
|
c = getFirstChunk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Chunk appendChunk(int length) {
|
// Must be called with lock held on sPool.
|
||||||
if (length < mMinCapacity) {
|
private void processPoolLocked() {
|
||||||
length = mMinCapacity;
|
while (true) {
|
||||||
}
|
SoftReference<Chunk> entry = (SoftReference<Chunk>) sQueue.poll();
|
||||||
|
if (entry == null) {
|
||||||
Chunk c;
|
break;
|
||||||
if (mChunks.isEmpty()) {
|
|
||||||
c = obtainChunk(length);
|
|
||||||
} else {
|
|
||||||
c = mChunks.getLast();
|
|
||||||
if (c.mLength == c.mArray.length) {
|
|
||||||
c = obtainChunk(length);
|
|
||||||
}
|
}
|
||||||
|
sPool.remove(entry);
|
||||||
}
|
}
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Chunk obtainChunk(int length) {
|
private Chunk obtainChunk(int length) {
|
||||||
Chunk c;
|
// Correct a small length.
|
||||||
if (mPool.isEmpty()) {
|
if (length < DEFAULT_CAPACITY) {
|
||||||
c = new Chunk(length);
|
length = DEFAULT_CAPACITY;
|
||||||
} else {
|
}
|
||||||
c = mPool.removeFirst();
|
synchronized (sPool) {
|
||||||
|
// Process any queued references so that sPool does not contain
|
||||||
|
// dead entries.
|
||||||
|
processPoolLocked();
|
||||||
|
if (!sPool.isEmpty()) {
|
||||||
|
return sPool.removeFirst().get();
|
||||||
|
} else {
|
||||||
|
return new Chunk(length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mChunks.addLast(c);
|
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Chunk {
|
public static class Chunk {
|
||||||
@@ -153,5 +133,19 @@ class ByteArrayBuilder {
|
|||||||
mArray = new byte[length];
|
mArray = new byte[length];
|
||||||
mLength = 0;
|
mLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the chunk and make it available for reuse.
|
||||||
|
*/
|
||||||
|
public void release() {
|
||||||
|
mLength = 0;
|
||||||
|
synchronized (sPool) {
|
||||||
|
// Add the chunk back to the pool as a SoftReference so it can
|
||||||
|
// be gc'd if needed.
|
||||||
|
sPool.offer(new SoftReference<Chunk>(this, sQueue));
|
||||||
|
sPool.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class LoadListener extends Handler implements EventHandler {
|
|||||||
|
|
||||||
private static int sNativeLoaderCount;
|
private static int sNativeLoaderCount;
|
||||||
|
|
||||||
private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder(8192);
|
private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder();
|
||||||
|
|
||||||
private String mUrl;
|
private String mUrl;
|
||||||
private WebAddress mUri;
|
private WebAddress mUri;
|
||||||
@@ -522,17 +522,18 @@ class LoadListener extends Handler implements EventHandler {
|
|||||||
* IMPORTANT: as this is called from network thread, can't call native
|
* IMPORTANT: as this is called from network thread, can't call native
|
||||||
* directly
|
* directly
|
||||||
* XXX: Unlike the other network thread methods, this method can do the
|
* XXX: Unlike the other network thread methods, this method can do the
|
||||||
* work of decoding the data and appending it to the data builder because
|
* work of decoding the data and appending it to the data builder.
|
||||||
* mDataBuilder is a thread-safe structure.
|
|
||||||
*/
|
*/
|
||||||
public void data(byte[] data, int length) {
|
public void data(byte[] data, int length) {
|
||||||
if (DebugFlags.LOAD_LISTENER) {
|
if (DebugFlags.LOAD_LISTENER) {
|
||||||
Log.v(LOGTAG, "LoadListener.data(): url: " + url());
|
Log.v(LOGTAG, "LoadListener.data(): url: " + url());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize on mData because commitLoad may write mData to WebCore
|
// The reason isEmpty() and append() need to synchronized together is
|
||||||
// and we don't want to replace mData or mDataLength at the same time
|
// because it is possible for getFirstChunk() to be called multiple
|
||||||
// as a write.
|
// times between isEmpty() and append(). This could cause commitLoad()
|
||||||
|
// to finish before processing the newly appended data and no message
|
||||||
|
// will be sent.
|
||||||
boolean sendMessage = false;
|
boolean sendMessage = false;
|
||||||
synchronized (mDataBuilder) {
|
synchronized (mDataBuilder) {
|
||||||
sendMessage = mDataBuilder.isEmpty();
|
sendMessage = mDataBuilder.isEmpty();
|
||||||
@@ -1009,28 +1010,34 @@ class LoadListener extends Handler implements EventHandler {
|
|||||||
if (mIsMainPageLoader) {
|
if (mIsMainPageLoader) {
|
||||||
String type = sCertificateTypeMap.get(mMimeType);
|
String type = sCertificateTypeMap.get(mMimeType);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
// In the case of downloading certificate, we will save it to
|
// This must be synchronized so that no more data can be added
|
||||||
// the KeyStore and stop the current loading so that it will not
|
// after getByteSize returns.
|
||||||
// generate a new history page
|
synchronized (mDataBuilder) {
|
||||||
byte[] cert = new byte[mDataBuilder.getByteSize()];
|
// In the case of downloading certificate, we will save it
|
||||||
int offset = 0;
|
// to the KeyStore and stop the current loading so that it
|
||||||
while (true) {
|
// will not generate a new history page
|
||||||
ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk();
|
byte[] cert = new byte[mDataBuilder.getByteSize()];
|
||||||
if (c == null) break;
|
int offset = 0;
|
||||||
|
while (true) {
|
||||||
|
ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk();
|
||||||
|
if (c == null) break;
|
||||||
|
|
||||||
if (c.mLength != 0) {
|
if (c.mLength != 0) {
|
||||||
System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
|
System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
|
||||||
offset += c.mLength;
|
offset += c.mLength;
|
||||||
|
}
|
||||||
|
c.release();
|
||||||
}
|
}
|
||||||
mDataBuilder.releaseChunk(c);
|
CertTool.addCertificate(mContext, type, cert);
|
||||||
|
mBrowserFrame.stopLoading();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
CertTool.addCertificate(mContext, type, cert);
|
|
||||||
mBrowserFrame.stopLoading();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give the data to WebKit now
|
// Give the data to WebKit now. We don't have to synchronize on
|
||||||
|
// mDataBuilder here because pulling each chunk removes it from the
|
||||||
|
// internal list so it cannot be modified.
|
||||||
PerfChecker checker = new PerfChecker();
|
PerfChecker checker = new PerfChecker();
|
||||||
ByteArrayBuilder.Chunk c;
|
ByteArrayBuilder.Chunk c;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -1047,7 +1054,7 @@ class LoadListener extends Handler implements EventHandler {
|
|||||||
}
|
}
|
||||||
nativeAddData(c.mArray, c.mLength);
|
nativeAddData(c.mArray, c.mLength);
|
||||||
}
|
}
|
||||||
mDataBuilder.releaseChunk(c);
|
c.release();
|
||||||
checker.responseAlert("res nativeAddData");
|
checker.responseAlert("res nativeAddData");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user