diff options
| author | Jeff Sharkey <jsharkey@android.com> | 2020-10-15 14:33:04 -0600 |
|---|---|---|
| committer | Jeff Sharkey <jsharkey@android.com> | 2020-10-16 13:44:16 -0600 |
| commit | af7c5f95cb963fcf482b84af434776337f69dbad (patch) | |
| tree | 580e584c785c4e8948b268b52648e74b4059658e /core/java | |
| parent | a3e52bf4e492786d3937d8926ea447e310f6ec98 (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.java | 88 | ||||
| -rw-r--r-- | core/java/com/android/internal/util/Preconditions.java | 66 |
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)); + } } /** |
