summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2020-10-15 14:33:04 -0600
committerJeff Sharkey <jsharkey@android.com>2020-10-16 13:44:16 -0600
commitaf7c5f95cb963fcf482b84af434776337f69dbad (patch)
tree580e584c785c4e8948b268b52648e74b4059658e /core/java
parenta3e52bf4e492786d3937d8926ea447e310f6ec98 (diff)
Simple alternative to String.format().
This purposefully supports only a small handful of substitutions to improve execution speed. Benchmarking reveals this optimized alternative performs 6.5x faster for a typical format string. Add Preconditions overloads that accept varargs to avoid string formatting overhead in successful case. Bug: 170978902 Test: atest FrameworksCoreTests:android.text.TextUtilsTest Test: ./frameworks/base/libs/hwui/tests/scripts/prep_generic.sh little && atest CorePerfTests:android.text.TextUtilsPerfTest Change-Id: I3fae4dc95cfc98a61c4a7f07ca0781c4a2ee3be9
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/text/TextUtils.java88
-rw-r--r--core/java/com/android/internal/util/Preconditions.java66
2 files changed, 145 insertions, 9 deletions
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 984acfd2c860..0cbb5c62eeb5 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -2080,6 +2080,94 @@ public class TextUtils {
}
/**
+ * Simple alternative to {@link String#format} which purposefully supports
+ * only a small handful of substitutions to improve execution speed.
+ * Benchmarking reveals this optimized alternative performs 6.5x faster for
+ * a typical format string.
+ * <p>
+ * Below is a summary of the limited grammar supported by this method; if
+ * you need advanced features, please continue using {@link String#format}.
+ * <ul>
+ * <li>{@code %b} for {@code boolean}
+ * <li>{@code %c} for {@code char}
+ * <li>{@code %d} for {@code int} or {@code long}
+ * <li>{@code %f} for {@code float} or {@code double}
+ * <li>{@code %s} for {@code String}
+ * <li>{@code %x} for hex representation of {@code int} or {@code long}
+ * <li>{@code %%} for literal {@code %}
+ * </ul>
+ *
+ * @throws IllegalArgumentException if the format string or arguments don't
+ * match the supported grammar described above.
+ * @hide
+ */
+ public static @NonNull String formatSimple(@NonNull String format, Object... args) {
+ final StringBuilder sb = new StringBuilder(format);
+ int j = 0;
+ for (int i = 0; i < sb.length(); ) {
+ if (sb.charAt(i) == '%') {
+ final String repl;
+ final char code = sb.charAt(i + 1);
+ switch (code) {
+ case 'b': {
+ if (j == args.length) {
+ throw new IllegalArgumentException("Too few arguments");
+ }
+ final Object arg = args[j++];
+ if (arg instanceof Boolean) {
+ repl = Boolean.toString((boolean) arg);
+ } else {
+ repl = Boolean.toString(arg != null);
+ }
+ break;
+ }
+ case 'c':
+ case 'd':
+ case 'f':
+ case 's': {
+ if (j == args.length) {
+ throw new IllegalArgumentException("Too few arguments");
+ }
+ final Object arg = args[j++];
+ repl = String.valueOf(arg);
+ break;
+ }
+ case 'x': {
+ if (j == args.length) {
+ throw new IllegalArgumentException("Too few arguments");
+ }
+ final Object arg = args[j++];
+ if (arg instanceof Integer) {
+ repl = Integer.toHexString((int) arg);
+ } else if (arg instanceof Long) {
+ repl = Long.toHexString((long) arg);
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported hex type " + arg.getClass());
+ }
+ break;
+ }
+ case '%': {
+ repl = "%";
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unsupported format code " + code);
+ }
+ }
+ sb.replace(i, i + 2, repl);
+ i += repl.length();
+ } else {
+ i++;
+ }
+ }
+ if (j != args.length) {
+ throw new IllegalArgumentException("Too many arguments");
+ }
+ return sb.toString();
+ }
+
+ /**
* Returns whether or not the specified spanned text has a style span.
* @hide
*/
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index dae649a903d5..e80e5454f40e 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -31,6 +31,12 @@ import java.util.Objects;
*/
public class Preconditions {
+ /**
+ * Ensures that an expression checking an argument is true.
+ *
+ * @param expression the expression to check
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
@UnsupportedAppUsage
public static void checkArgument(boolean expression) {
if (!expression) {
@@ -62,8 +68,9 @@ public class Preconditions {
* @param messageArgs arguments for {@code messageTemplate}
* @throws IllegalArgumentException if {@code expression} is false
*/
- public static void checkArgument(boolean expression,
- final String messageTemplate,
+ public static void checkArgument(
+ final boolean expression,
+ final @NonNull String messageTemplate,
final Object... messageArgs) {
if (!expression) {
throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
@@ -114,7 +121,9 @@ public class Preconditions {
* @throws IllegalArgumentException if {@code string} is empty
*/
public static @NonNull <T extends CharSequence> T checkStringNotEmpty(
- final T string, final String messageTemplate, final Object... messageArgs) {
+ final T string,
+ final @NonNull String messageTemplate,
+ final Object... messageArgs) {
if (TextUtils.isEmpty(string)) {
throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
}
@@ -160,17 +169,49 @@ public class Preconditions {
}
/**
+ * Ensures that an object reference passed as a parameter to the calling
+ * method is not null.
+ *
+ * @param messageTemplate a printf-style message template to use if the check fails; will
+ * be converted to a string using {@link String#format(String, Object...)}
+ * @param messageArgs arguments for {@code messageTemplate}
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static @NonNull <T> T checkNotNull(
+ final T reference,
+ final @NonNull String messageTemplate,
+ final Object... messageArgs) {
+ if (reference == null) {
+ throw new NullPointerException(String.format(messageTemplate, messageArgs));
+ }
+ return reference;
+ }
+
+ /**
* Ensures the truth of an expression involving the state of the calling
* instance, but not involving any parameters to the calling method.
*
* @param expression a boolean expression
- * @param message exception message
* @throws IllegalStateException if {@code expression} is false
*/
@UnsupportedAppUsage
- public static void checkState(final boolean expression, String message) {
+ public static void checkState(final boolean expression) {
+ checkState(expression, null);
+ }
+
+ /**
+ * Ensures the truth of an expression involving the state of the calling
+ * instance, but not involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @throws IllegalStateException if {@code expression} is false
+ */
+ @UnsupportedAppUsage
+ public static void checkState(final boolean expression, String errorMessage) {
if (!expression) {
- throw new IllegalStateException(message);
+ throw new IllegalStateException(errorMessage);
}
}
@@ -179,11 +220,18 @@ public class Preconditions {
* instance, but not involving any parameters to the calling method.
*
* @param expression a boolean expression
+ * @param messageTemplate a printf-style message template to use if the check fails; will
+ * be converted to a string using {@link String#format(String, Object...)}
+ * @param messageArgs arguments for {@code messageTemplate}
* @throws IllegalStateException if {@code expression} is false
*/
- @UnsupportedAppUsage
- public static void checkState(final boolean expression) {
- checkState(expression, null);
+ public static void checkState(
+ final boolean expression,
+ final @NonNull String messageTemplate,
+ final Object... messageArgs) {
+ if (!expression) {
+ throw new IllegalStateException(String.format(messageTemplate, messageArgs));
+ }
}
/**