Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 51 additions & 10 deletions android/guava/src/com/google/common/base/CharMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -727,18 +727,59 @@ public String replaceFrom(CharSequence sequence, CharSequence replacement) {
}

int len = string.length();
StringBuilder buf = new StringBuilder((len * 3 / 2) + 16);

int oldpos = 0;
// Count matches to pre-calculate the exact size of the destination array.
int count = 0;
int matchPos = pos;
do {
buf.append(string, oldpos, pos);
buf.append(replacement);
oldpos = pos + 1;
pos = indexIn(string, oldpos);
} while (pos != -1);

buf.append(string, oldpos, len);
return buf.toString();
count++;
matchPos = indexIn(string, matchPos + 1);
} while (matchPos != -1);

long newLen = (long) len + (long) count * (replacementLen - 1);
if (newLen > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Required length exceeds implementation limit");
}
int destSize = (int) newLen;

char[] dest = Platform.acquireCharBuffer();
boolean acquired = (dest != null);
if (dest == null) {
dest = new char[destSize];
} else if (dest.length < destSize) {
dest = new char[destSize];
}

try {
String replStr = replacement.toString();
int destIndex = 0;
int oldpos = 0;
matchPos = pos;

do {
int charsToCopy = matchPos - oldpos;
if (charsToCopy > 0) {
string.getChars(oldpos, matchPos, dest, destIndex);
destIndex += charsToCopy;
}
replStr.getChars(0, replacementLen, dest, destIndex);
destIndex += replacementLen;
oldpos = matchPos + 1;
matchPos = indexIn(string, oldpos);
} while (matchPos != -1);

int charsLeft = len - oldpos;
if (charsLeft > 0) {
string.getChars(oldpos, len, dest, destIndex);
destIndex += charsLeft;
}

return new String(dest, 0, destIndex);
} finally {
if (acquired) {
Platform.releaseCharBuffer(dest);
}
}
}

/**
Expand Down
48 changes: 26 additions & 22 deletions android/guava/src/com/google/common/base/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,6 @@ static CharMatcher precomputeCharMatcher(CharMatcher matcher) {

static <T extends Enum<T>> Optional<T> getEnumIfPresent(Class<T> enumClass, String value) {
WeakReference<? extends Enum<?>> ref = Enums.getEnumConstants(enumClass).get(value);
/*
* We use `fromNullable` instead of `of` because `WeakReference.get()` has a nullable return
* type.
*
* In practice, we are very unlikely to see `null`: The `WeakReference` to the enum constant
* won't be cleared as long as the enum constant is referenced somewhere, and the enum constant
* is referenced somewhere for as long as the enum class is loaded. *Maybe in theory* the enum
* class could be unloaded after the above call to `getEnumConstants` but before we call
* `get()`, but that is vanishingly unlikely.
*/
return ref == null ? Optional.absent() : Optional.fromNullable(enumClass.cast(ref.get()));
}

Expand All @@ -58,22 +48,10 @@ static boolean stringIsNullOrEmpty(@Nullable String string) {
return string == null || string.isEmpty();
}

/**
* Returns the string if it is not null, or an empty string otherwise.
*
* @param string the string to test and possibly return
* @return {@code string} if it is not null; {@code ""} otherwise
*/
static String nullToEmpty(@Nullable String string) {
return (string == null) ? "" : string;
}

/**
* Returns the string if it is not empty, or a null string otherwise.
*
* @param string the string to test and possibly return
* @return {@code string} if it is not empty; {@code null} otherwise
*/
static @Nullable String emptyToNull(@Nullable String string) {
return stringIsNullOrEmpty(string) ? null : string;
}
Expand Down Expand Up @@ -116,4 +94,30 @@ public boolean isPcreLike() {
return true;
}
}

private static @Nullable ThreadLocal<char @Nullable []> destTl;

/** Acquires a thread-local 1024-char buffer if available, or returns null if busy. */
static char @Nullable [] acquireCharBuffer() {
ThreadLocal<char @Nullable []> tl = destTl;
if (tl == null) {
destTl = tl = new ThreadLocal<char @Nullable []>();
}
char[] buffer = tl.get();
if (buffer == null) {
return new char[1024];
}
tl.set(null);
return buffer;
}

/** Releases the acquired thread-local buffer. */
static void releaseCharBuffer(char[] buffer) {
if (buffer.length == 1024) {
ThreadLocal<char @Nullable []> tl = destTl;
if (tl != null) {
tl.set(buffer);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,22 @@ static String stringValueOf(@Nullable Object o) {
return String.valueOf(o);
}

private static final char[] CHAR_BUFFER = new char[1024];
private static boolean inUse = false;

/** Acquires the static buffer if available, or returns null if busy (re-entrant call). */
static char @Nullable [] acquireCharBuffer() {
if (inUse) {
return null;
}
inUse = true;
return CHAR_BUFFER;
}

/** Releases the acquired buffer. */
static void releaseCharBuffer(char[] buffer) {
inUse = false;
}

private Platform() {}
}
61 changes: 51 additions & 10 deletions guava/src/com/google/common/base/CharMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -727,18 +727,59 @@ public String replaceFrom(CharSequence sequence, CharSequence replacement) {
}

int len = string.length();
StringBuilder buf = new StringBuilder((len * 3 / 2) + 16);

int oldpos = 0;
// Count matches to pre-calculate the exact size of the destination array.
int count = 0;
int matchPos = pos;
do {
buf.append(string, oldpos, pos);
buf.append(replacement);
oldpos = pos + 1;
pos = indexIn(string, oldpos);
} while (pos != -1);

buf.append(string, oldpos, len);
return buf.toString();
count++;
matchPos = indexIn(string, matchPos + 1);
} while (matchPos != -1);

long newLen = (long) len + (long) count * (replacementLen - 1);
if (newLen > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Required length exceeds implementation limit");
}
int destSize = (int) newLen;

char[] dest = Platform.acquireCharBuffer();
boolean acquired = (dest != null);
if (dest == null) {
dest = new char[destSize];
} else if (dest.length < destSize) {
dest = new char[destSize];
}

try {
String replStr = replacement.toString();
int destIndex = 0;
int oldpos = 0;
matchPos = pos;

do {
int charsToCopy = matchPos - oldpos;
if (charsToCopy > 0) {
string.getChars(oldpos, matchPos, dest, destIndex);
destIndex += charsToCopy;
}
replStr.getChars(0, replacementLen, dest, destIndex);
destIndex += replacementLen;
oldpos = matchPos + 1;
matchPos = indexIn(string, oldpos);
} while (matchPos != -1);

int charsLeft = len - oldpos;
if (charsLeft > 0) {
string.getChars(oldpos, len, dest, destIndex);
destIndex += charsLeft;
}

return new String(dest, 0, destIndex);
} finally {
if (acquired) {
Platform.releaseCharBuffer(dest);
}
}
}

/**
Expand Down
26 changes: 26 additions & 0 deletions guava/src/com/google/common/base/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,30 @@ public boolean isPcreLike() {
return true;
}
}

private static @Nullable ThreadLocal<char @Nullable []> destTl;

/** Acquires a thread-local 1024-char buffer if available, or returns null if busy. */
static char @Nullable [] acquireCharBuffer() {
ThreadLocal<char @Nullable []> tl = destTl;
if (tl == null) {
destTl = tl = new ThreadLocal<char @Nullable []>();
}
char[] buffer = tl.get();
if (buffer == null) {
return new char[1024];
}
tl.set(null);
return buffer;
}

/** Releases the acquired thread-local buffer. */
static void releaseCharBuffer(char[] buffer) {
if (buffer.length == 1024) {
ThreadLocal<char @Nullable []> tl = destTl;
if (tl != null) {
tl.set(buffer);
}
}
}
}
Loading