Speed up Uri.getQueryParameter by allocating less.

Sample hierarchial URL, from my ContactsProvider test case:

content://com.android.contacts/data?account_name=braddroid%40gmail.com&account_type=com.google.GAIA&caller_is_syncadapter=true

Without this patch:  (fetching the "account_name" parameter)

10000 iters: 0.5293 ms average
10000 iters: 0.5119 ms average
10000 iters: 0.5158 ms average

With this patch, rewriting it to not allocate memory (no implicit
StringBuilder), but still no caching:

1) when it needs to decode something (i.e account_name above;
   allocates memory)

  50000 iters: 0.28724 ms average
  50000 iters: 0.31774 ms average
  50000 iters: 0.28764 ms average

2) when it doesn't need to decode (and thus allocate memory,
   i.e. account_type above)

  50000 iters: 0.0954 ms average
  50000 iters: 0.09124 ms average
  50000 iters: 0.09088 ms average
This commit is contained in:
Brad Fitzpatrick
2009-10-26 14:03:54 -07:00
parent 52c931b7dd
commit 70ece8d334
2 changed files with 54 additions and 36 deletions

View File

@ -1567,51 +1567,40 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
if (isOpaque()) {
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
}
if (key == null) {
throw new NullPointerException("key");
}
String query = getEncodedQuery();
final String query = getEncodedQuery();
if (query == null) {
return null;
}
String encodedKey;
try {
encodedKey = URLEncoder.encode(key, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
final String encodedKey = encode(key, null);
final int encodedKeyLength = encodedKey.length();
String prefix = encodedKey + "=";
int encodedKeySearchIndex = 0;
final int encodedKeySearchEnd = query.length() - (encodedKeyLength + 1);
if (query.length() < prefix.length()) {
return null;
}
int start;
if (query.startsWith(prefix)) {
// It's the first parameter.
start = prefix.length();
} else {
// It must be later in the query string.
prefix = "&" + prefix;
start = query.indexOf(prefix);
if (start == -1) {
// Not found.
return null;
while (encodedKeySearchIndex <= encodedKeySearchEnd) {
int keyIndex = query.indexOf(encodedKey, encodedKeySearchIndex);
if (keyIndex == -1) {
break;
}
final int equalsIndex = keyIndex + encodedKeyLength;
if (query.charAt(equalsIndex) != '=') {
encodedKeySearchIndex = equalsIndex + 1;
continue;
}
if (keyIndex == 0 || query.charAt(keyIndex - 1) == '&') {
int end = query.indexOf('&', equalsIndex);
if (end == -1) {
end = query.length();
}
return decode(query.substring(equalsIndex + 1, end));
}
start += prefix.length();
}
// Find end of value.
int end = query.indexOf('&', start);
if (end == -1) {
end = query.length();
}
String value = query.substring(start, end);
return decode(value);
return null;
}
/** Identifies a null parcelled Uri. */

View File

@ -541,4 +541,33 @@ public class UriTest extends TestCase {
assertEquals(nestedUrl,
Uri.decode(uri.getQueryParameters("nested").get(0)));
}
public void testGetQueryParameterEdgeCases() {
Uri uri;
// key at beginning of URL
uri = Uri.parse("http://test/").buildUpon()
.appendQueryParameter("key", "a b")
.appendQueryParameter("keya", "c d")
.appendQueryParameter("bkey", "e f")
.build();
assertEquals("a b", uri.getQueryParameter("key"));
// key in middle of URL
uri = Uri.parse("http://test/").buildUpon()
.appendQueryParameter("akeyb", "a b")
.appendQueryParameter("keya", "c d")
.appendQueryParameter("key", "e f")
.appendQueryParameter("bkey", "g h")
.build();
assertEquals("e f", uri.getQueryParameter("key"));
// key at end of URL
uri = Uri.parse("http://test/").buildUpon()
.appendQueryParameter("akeyb", "a b")
.appendQueryParameter("keya", "c d")
.appendQueryParameter("key", "y z")
.build();
assertEquals("y z", uri.getQueryParameter("key"));
}
}