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
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package com.google.common.base;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(NANOSECONDS)
@State(Scope.Benchmark)
@Fork(5)
public class StringUtilJmhBenchmark {
@Param({"100", "1000"})
private int length;

@Param({"ASCII", "CJK", "SPECIAL"})
private String type;

private String input;

@Setup
public void setUp() {
StringBuilder sb = new StringBuilder(length);
if ("SPECIAL".equals(type)) {
int[] specialChars = {
'a',
0xAD,
0x600,
0x202A,
0xFFF9,
0x1D173,
0xE0001, // In the set
0x101,
0x700,
0x3001,
0x1D172,
0xE0002 // Not in the set
};
for (int i = 0; i < length; i++) {
sb.appendCodePoint(specialChars[i % specialChars.length]);
}
} else {
for (int i = 0; i < length; i++) {
if ("ASCII".equals(type)) {
sb.append((char) ('a' + (i % 26)));
} else {
sb.append((char) (0x4e00 + (i % 1000))); // Chinese ideographs
}
}
}
input = sb.toString();
}

@Benchmark
public String escapeUnicodeCurrent() {
return StringUtil.javaScriptEscape(input);
}

@Benchmark
public String escapeUnicodeOriginal() {
return originalJavaScriptEscape(input, false);
}

@Benchmark
public String escapeAsciiCurrent() {
return StringUtil.javaScriptEscapeToAscii(input);
}

@Benchmark
public String escapeAsciiOriginal() {
return originalJavaScriptEscape(input, true);
}

private static String originalJavaScriptEscape(CharSequence s, boolean escapeToAscii) {
StringBuilder sb = new StringBuilder(s.length() * 9 / 8);
try {
originalEscapeStringBody(s, escapeToAscii, StringUtil.JsEscapingMode.EMBEDDABLE_JS, sb);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return sb.toString();
}

private static void originalEscapeStringBody(
CharSequence plainText,
boolean escapeToAscii,
StringUtil.JsEscapingMode jsEscapingMode,
Appendable out)
throws IOException {
int pos = 0;
int len = plainText.length();
for (int codePoint, charCount, i = 0; i < len; i += charCount) {
codePoint = Character.codePointAt(plainText, i);
charCount = Character.charCount(codePoint);

if (!originalShouldEscapeChar(codePoint, escapeToAscii, jsEscapingMode)) {
continue;
}

out.append(plainText, pos, i);
pos = i + charCount;
switch (codePoint) {
case '\b':
out.append("\\b");
break;
case '\t':
out.append("\\t");
break;
case '\n':
out.append("\\n");
break;
case '\f':
out.append("\\f");
break;
case '\r':
out.append("\\r");
break;
case '\\':
out.append("\\\\");
break;
case '"':
case '\'':
if (jsEscapingMode == StringUtil.JsEscapingMode.JSON && codePoint == '\'') {
out.append((char) codePoint);
break;
} else if (jsEscapingMode != StringUtil.JsEscapingMode.EMBEDDABLE_JS) {
out.append('\\').append((char) codePoint);
break;
}
// fall through
default:
if (codePoint >= 0x100 || jsEscapingMode == StringUtil.JsEscapingMode.JSON) {
appendUnicode(codePoint, out);
} else {
appendHex((char) codePoint, out);
}
break;
}
}
out.append(plainText, pos, len);
}

private static boolean originalShouldEscapeChar(
int codePoint, boolean escapeToAscii, StringUtil.JsEscapingMode jsEscapingMode) {
if (escapeToAscii && (codePoint < 0x20 || codePoint > 0x7e)) {
return true;
}
return ORIGINAL_JS_ESCAPE_CHARS.contains(codePoint);
}

private static final char[] hexChars = "0123456789abcdef".toCharArray();

private static void appendHex(char ch, Appendable out) throws IOException {
out.append("\\x").append(hexChars[(ch >>> 4) & 0xf]).append(hexChars[ch & 0xf]);
}

private static void appendUnicode(int codePoint, Appendable out) throws IOException {
if (Character.isSupplementaryCodePoint(codePoint)) {
char[] surrogates = Character.toChars(codePoint);
appendUnicode(surrogates[0], out);
appendUnicode(surrogates[1], out);
return;
}
out.append("\\u")
.append(hexChars[(codePoint >>> 12) & 0xf])
.append(hexChars[(codePoint >>> 8) & 0xf])
.append(hexChars[(codePoint >>> 4) & 0xf])
.append(hexChars[codePoint & 0xf]);
}

private static final class BoxedCodePointSet {
final boolean[] fastArray;
final Set<Integer> elements;

BoxedCodePointSet(Set<Integer> codePoints) {
this.elements = codePoints;
fastArray = new boolean[0x100];
for (int i = 0; i < fastArray.length; i++) {
fastArray[i] = elements.contains(i);
}
}

boolean contains(int codePoint) {
if (codePoint < fastArray.length) {
return fastArray[codePoint];
}
return elements.contains(codePoint);
}
}

private static final BoxedCodePointSet ORIGINAL_JS_ESCAPE_CHARS;

static {
Set<Integer> set = new HashSet<>();
set.add(0xAD);
for (int i = 0x600; i <= 0x603; i++) set.add(i);
set.add(0x6DD);
set.add(0x070F);
for (int i = 0x17B4; i <= 0x17B5; i++) set.add(i);
for (int i = 0x200B; i <= 0x200F; i++) set.add(i);
for (int i = 0x202A; i <= 0x202E; i++) set.add(i);
for (int i = 0x2028; i <= 0x2029; i++) set.add(i);
for (int i = 0x2060; i <= 0x2064; i++) set.add(i);
for (int i = 0x206A; i <= 0x206F; i++) set.add(i);
set.add(0xFEFF);
for (int i = 0xFFF9; i <= 0xFFFB; i++) set.add(i);
for (int i = 0x1D173; i <= 0x1D17A; i++) set.add(i);
set.add(0xE0001);
for (int i = 0xE0020; i <= 0xE007F; i++) set.add(i);
set.add(0x0000);
set.add(0x000A);
set.add(0x000D);
set.add(0x0085);
set.add((int) '\'');
set.add((int) '\"');
set.add((int) '&');
set.add((int) '<');
set.add((int) '>');
set.add((int) '=');
set.add((int) '\\');
ORIGINAL_JS_ESCAPE_CHARS = new BoxedCodePointSet(set);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
* @since 2.0
*/
@GwtCompatible
@SuppressWarnings({
"TooManyParameters",
"AssignmentExpression"
}) // fundamental factory methods and concise assignments
public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> implements BiMap<K, V> {

/**
Expand Down Expand Up @@ -527,6 +531,8 @@ public static <K, V> ImmutableBiMap<K, V> copyOf(Map<? extends K, ? extends V> m
return bimap;
}
}
// Map.forEach is avoided in the Android java7 branch to maintain compatibility with Android
// APIs < 24.
return copyOf(map.entrySet());
}

Expand Down
19 changes: 13 additions & 6 deletions android/guava/src/com/google/common/collect/ImmutableMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@
*/
@DoNotMock("Use ImmutableMap.of or another implementation")
@GwtCompatible
@SuppressWarnings("serial") // we're overriding default serialization
@SuppressWarnings({
"serial",
"TooManyParameters",
"AssignmentExpression"
}) // overriding serialization, fundamental factory methods, and concise assignments
public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {

/**
Expand Down Expand Up @@ -456,7 +460,6 @@ public Builder() {
this(ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY);
}

@SuppressWarnings({"unchecked", "rawtypes"})
Builder(int initialCapacity) {
this.alternatingKeysAndValues = new @Nullable Object[2 * initialCapacity];
this.size = 0;
Expand Down Expand Up @@ -510,6 +513,8 @@ public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
*/
@CanIgnoreReturnValue
public Builder<K, V> putAll(Map<? extends K, ? extends V> map) {
// Map.forEach is avoided in the Android java7 branch to maintain compatibility with Android
// APIs < 24.
return putAll(map.entrySet());
}

Expand Down Expand Up @@ -655,12 +660,12 @@ static <V> void sortEntries(
@Nullable Object[] alternatingKeysAndValues,
int size,
Comparator<? super V> valueComparator) {
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings({"rawtypes", "unchecked"}) // safe array allocation for sorting
Entry<Object, V>[] entries = new Entry[size];
for (int i = 0; i < size; i++) {
// requireNonNull is safe because the first `2*size` elements have been filled in.
Object key = requireNonNull(alternatingKeysAndValues[2 * i]);
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") // safe cast to value type
V value = (V) requireNonNull(alternatingKeysAndValues[2 * i + 1]);
entries[i] = new AbstractMap.SimpleImmutableEntry<Object, V>(key, value);
}
Expand Down Expand Up @@ -736,6 +741,8 @@ public static <K, V> ImmutableMap<K, V> copyOf(Map<? extends K, ? extends V> map
return kvMap;
}
}
// Map.forEach is avoided in the Android java7 branch to maintain compatibility with Android
// APIs < 24.
return copyOf(map.entrySet());
}

Expand Down Expand Up @@ -1150,7 +1157,7 @@ static class SerializedForm<K, V> implements Serializable {
this.values = map.values();
}

@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") // safe deserialization resolution
final Object readResolve() {
if (!(this.keys instanceof ImmutableSet)) {
return legacyReadResolve();
Expand All @@ -1171,7 +1178,7 @@ final Object readResolve() {
return builder.buildOrThrow();
}

@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") // safe legacy deserialization resolution
final Object legacyReadResolve() {
K[] keys = (K[]) this.keys;
V[] values = (V[]) this.values;
Expand Down
Loading
Loading