Kill the StringBuilderPool.
The intention may have been nice originally but these end up being copied anyway :/ Let's remove them now, and in a later change, just keep references to the created objects. Change-Id: Ifba8357c20384f9eb40cd916665ed1fc6dc8cab1main
parent
95fcb0cce9
commit
adf218eed5
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2011 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.android.inputmethod.latin;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A pool of string builders to be used from anywhere.
|
|
||||||
*/
|
|
||||||
public class StringBuilderPool {
|
|
||||||
// Singleton
|
|
||||||
private static final StringBuilderPool sInstance = new StringBuilderPool();
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
private StringBuilderPool() {}
|
|
||||||
// TODO: Make this a normal array with a size of 20, or a ConcurrentQueue
|
|
||||||
private final List<StringBuilder> mPool =
|
|
||||||
Collections.synchronizedList(new ArrayList<StringBuilder>());
|
|
||||||
|
|
||||||
public static StringBuilder getStringBuilder(final int initialSize) {
|
|
||||||
// TODO: although the pool is synchronized, the following is not thread-safe.
|
|
||||||
// Two threads entering this at the same time could take the same size of the pool and the
|
|
||||||
// second to attempt removing this index from the pool would crash with an
|
|
||||||
// IndexOutOfBoundsException.
|
|
||||||
// At the moment this pool is only used in Suggest.java and only in one thread so it's
|
|
||||||
// okay. The simplest thing to do here is probably to replace the ArrayList with a
|
|
||||||
// ConcurrentQueue.
|
|
||||||
final int poolSize = sInstance.mPool.size();
|
|
||||||
final StringBuilder sb = poolSize > 0 ? (StringBuilder) sInstance.mPool.remove(poolSize - 1)
|
|
||||||
: new StringBuilder(initialSize);
|
|
||||||
sb.setLength(0);
|
|
||||||
return sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void recycle(final StringBuilder garbage) {
|
|
||||||
if (DEBUG) {
|
|
||||||
final int gid = garbage.hashCode();
|
|
||||||
for (final StringBuilder q : sInstance.mPool) {
|
|
||||||
if (gid == q.hashCode()) throw new RuntimeException("Duplicate id " + gid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sInstance.mPool.add(garbage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ensureCapacity(final int capacity, final int initialSize) {
|
|
||||||
for (int i = sInstance.mPool.size(); i < capacity; ++i) {
|
|
||||||
final StringBuilder sb = new StringBuilder(initialSize);
|
|
||||||
sInstance.mPool.add(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getSize() {
|
|
||||||
return sInstance.mPool.size();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -142,7 +142,7 @@ public class StringUtils {
|
||||||
for (int j = 0; j < i; j++) {
|
for (int j = 0; j < i; j++) {
|
||||||
CharSequence previous = suggestions.get(j);
|
CharSequence previous = suggestions.get(j);
|
||||||
if (TextUtils.equals(cur, previous)) {
|
if (TextUtils.equals(cur, previous)) {
|
||||||
removeFromSuggestions(suggestions, i);
|
suggestions.remove(i);
|
||||||
i--;
|
i--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -151,14 +151,6 @@ public class StringUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void removeFromSuggestions(final ArrayList<CharSequence> suggestions,
|
|
||||||
final int index) {
|
|
||||||
final CharSequence garbage = suggestions.remove(index);
|
|
||||||
if (garbage instanceof StringBuilder) {
|
|
||||||
StringBuilderPool.recycle((StringBuilder)garbage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
|
public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
|
||||||
if (returnsNameInThisLocale) {
|
if (returnsNameInThisLocale) {
|
||||||
return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
|
return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
private int[] mBigramScores = new int[PREF_MAX_BIGRAMS];
|
private int[] mBigramScores = new int[PREF_MAX_BIGRAMS];
|
||||||
|
|
||||||
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
|
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
|
||||||
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
|
private ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
|
||||||
private CharSequence mConsideredWord;
|
private CharSequence mConsideredWord;
|
||||||
|
|
||||||
// TODO: Remove these member variables by passing more context to addWord() callback method
|
// TODO: Remove these member variables by passing more context to addWord() callback method
|
||||||
|
@ -122,7 +122,6 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
|
private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
|
||||||
mWhiteListDictionary = new WhitelistDictionary(context, locale);
|
mWhiteListDictionary = new WhitelistDictionary(context, locale);
|
||||||
addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
|
addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
|
||||||
StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initAsynchronously(final Context context, final int dictionaryResId,
|
private void initAsynchronously(final Context context, final int dictionaryResId,
|
||||||
|
@ -229,14 +228,13 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
mPrefMaxSuggestions = maxSuggestions;
|
mPrefMaxSuggestions = maxSuggestions;
|
||||||
mScores = new int[mPrefMaxSuggestions];
|
mScores = new int[mPrefMaxSuggestions];
|
||||||
mBigramScores = new int[PREF_MAX_BIGRAMS];
|
mBigramScores = new int[PREF_MAX_BIGRAMS];
|
||||||
collectGarbage(mSuggestions, mPrefMaxSuggestions);
|
mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions);
|
||||||
StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) {
|
private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) {
|
||||||
if (TextUtils.isEmpty(word) || !(all || first)) return word;
|
if (TextUtils.isEmpty(word) || !(all || first)) return word;
|
||||||
final int wordLength = word.length();
|
final int wordLength = word.length();
|
||||||
final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength());
|
final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
|
||||||
// TODO: Must pay attention to locale when changing case.
|
// TODO: Must pay attention to locale when changing case.
|
||||||
if (all) {
|
if (all) {
|
||||||
sb.append(word.toString().toUpperCase());
|
sb.append(word.toString().toUpperCase());
|
||||||
|
@ -250,12 +248,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addBigramToSuggestions(CharSequence bigram) {
|
protected void addBigramToSuggestions(CharSequence bigram) {
|
||||||
// TODO: Try to be a little more shrewd with resource allocation.
|
final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
|
||||||
// At the moment we copy this object because the StringBuilders are pooled (see
|
|
||||||
// StringBuilderPool.java) and when we are finished using mSuggestions and
|
|
||||||
// mBigramSuggestions we will take everything from both and insert them back in the
|
|
||||||
// pool, so we can't allow the same object to be in both lists at the same time.
|
|
||||||
final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength());
|
|
||||||
sb.append(bigram);
|
sb.append(bigram);
|
||||||
mSuggestions.add(sb);
|
mSuggestions.add(sb);
|
||||||
}
|
}
|
||||||
|
@ -266,7 +259,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
mIsFirstCharCapitalized = false;
|
mIsFirstCharCapitalized = false;
|
||||||
mIsAllUpperCase = false;
|
mIsAllUpperCase = false;
|
||||||
mTrailingSingleQuotesCount = 0;
|
mTrailingSingleQuotesCount = 0;
|
||||||
collectGarbage(mSuggestions, mPrefMaxSuggestions);
|
mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions);
|
||||||
Arrays.fill(mScores, 0);
|
Arrays.fill(mScores, 0);
|
||||||
|
|
||||||
// Treating USER_TYPED as UNIGRAM suggestion for logging now.
|
// Treating USER_TYPED as UNIGRAM suggestion for logging now.
|
||||||
|
@ -274,7 +267,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
mConsideredWord = "";
|
mConsideredWord = "";
|
||||||
|
|
||||||
Arrays.fill(mBigramScores, 0);
|
Arrays.fill(mBigramScores, 0);
|
||||||
collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS);
|
mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS);
|
||||||
|
|
||||||
CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
|
CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
|
||||||
if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) {
|
if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) {
|
||||||
|
@ -305,7 +298,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
|
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
|
||||||
mIsAllUpperCase = wordComposer.isAllUpperCase();
|
mIsAllUpperCase = wordComposer.isAllUpperCase();
|
||||||
mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
|
mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
|
||||||
collectGarbage(mSuggestions, mPrefMaxSuggestions);
|
mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions);
|
||||||
Arrays.fill(mScores, 0);
|
Arrays.fill(mScores, 0);
|
||||||
|
|
||||||
final String typedWord = wordComposer.getTypedWord();
|
final String typedWord = wordComposer.getTypedWord();
|
||||||
|
@ -328,7 +321,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) {
|
if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) {
|
||||||
// At first character typed, search only the bigrams
|
// At first character typed, search only the bigrams
|
||||||
Arrays.fill(mBigramScores, 0);
|
Arrays.fill(mBigramScores, 0);
|
||||||
collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS);
|
mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS);
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(prevWordForBigram)) {
|
if (!TextUtils.isEmpty(prevWordForBigram)) {
|
||||||
CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
|
CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
|
||||||
|
@ -542,7 +535,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
|
|
||||||
System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
|
System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
|
||||||
sortedScores[pos] = score;
|
sortedScores[pos] = score;
|
||||||
final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength());
|
final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
|
||||||
// TODO: Must pay attention to locale when changing case.
|
// TODO: Must pay attention to locale when changing case.
|
||||||
if (mIsAllUpperCase) {
|
if (mIsAllUpperCase) {
|
||||||
sb.append(new String(word, offset, length).toUpperCase());
|
sb.append(new String(word, offset, length).toUpperCase());
|
||||||
|
@ -559,10 +552,7 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
}
|
}
|
||||||
suggestions.add(pos, sb);
|
suggestions.add(pos, sb);
|
||||||
if (suggestions.size() > prefMaxSuggestions) {
|
if (suggestions.size() > prefMaxSuggestions) {
|
||||||
final CharSequence garbage = suggestions.remove(prefMaxSuggestions);
|
suggestions.remove(prefMaxSuggestions);
|
||||||
if (garbage instanceof StringBuilder) {
|
|
||||||
StringBuilderPool.recycle((StringBuilder)garbage);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
|
LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
|
||||||
}
|
}
|
||||||
|
@ -589,24 +579,6 @@ public class Suggest implements Dictionary.WordCallback {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void collectGarbage(ArrayList<CharSequence> suggestions,
|
|
||||||
int prefMaxSuggestions) {
|
|
||||||
int poolSize = StringBuilderPool.getSize();
|
|
||||||
int garbageSize = suggestions.size();
|
|
||||||
while (poolSize < prefMaxSuggestions && garbageSize > 0) {
|
|
||||||
final CharSequence garbage = suggestions.get(garbageSize - 1);
|
|
||||||
if (garbage instanceof StringBuilder) {
|
|
||||||
StringBuilderPool.recycle((StringBuilder)garbage);
|
|
||||||
poolSize++;
|
|
||||||
}
|
|
||||||
garbageSize--;
|
|
||||||
}
|
|
||||||
if (poolSize == prefMaxSuggestions + 1) {
|
|
||||||
Log.w("Suggest", "String pool got too big: " + poolSize);
|
|
||||||
}
|
|
||||||
suggestions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
final Set<Dictionary> dictionaries = new HashSet<Dictionary>();
|
final Set<Dictionary> dictionaries = new HashSet<Dictionary>();
|
||||||
dictionaries.addAll(mUnigramDictionaries.values());
|
dictionaries.addAll(mUnigramDictionaries.values());
|
||||||
|
|
Loading…
Reference in New Issue