Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit faea5cda authored by William Escande's avatar William Escande
Browse files

AndroidFrameworkEfficientStrings: formatSimple

[AndroidFrameworkEfficientStrings] Simple format strings can be replaced
with TextUtils.formatSimple() for a 6x performance improvement

But since TextUtils doesn't expose this method, we have to integrate it
in the Bluetooth code directly

Bug: 344658662
Test: None
Flag: Exempt refactor
Change-Id: Ib407c721746ab423aa9bb1ec9236e8ebbc9787f9
parent 326867a0
Loading
Loading
Loading
Loading
+112 −0
Original line number Diff line number Diff line
@@ -1269,6 +1269,8 @@ public final class Utils {
     *
     * <p>(copied from framework/base/core/java/android/text/TextUtils.java)
     *
     * <p>(See {@code android.text.TextUtils.truncateStringForUtf8Storage}
     *
     * @param str a string
     * @param maxbytes the maximum number of UTF-8 encoded bytes
     * @return the beginning of the string, so that it uses at most maxbytes bytes in UTF-8
@@ -1300,4 +1302,114 @@ public final class Utils {
        }
        return str;
    }

    /**
     * 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 %}
     *   <li>{@code %04d} style grammar to specify the argument width, such as {@code %04d} to
     *       prefix an {@code int} with zeros or {@code %10b} to prefix a {@code boolean} with
     *       spaces
     * </ul>
     *
     * <p>(copied from framework/base/core/java/android/text/TextUtils.java)
     *
     * <p>See {@code android.text.TextUtils.formatSimple}
     *
     * @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) == '%') {
                char code = sb.charAt(i + 1);

                // Decode any argument width request
                char prefixChar = '\0';
                int prefixLen = 0;
                int consume = 2;
                while ('0' <= code && code <= '9') {
                    if (prefixChar == '\0') {
                        prefixChar = (code == '0') ? '0' : ' ';
                    }
                    prefixLen *= 10;
                    prefixLen += Character.digit(code, 10);
                    consume += 1;
                    code = sb.charAt(i + consume - 1);
                }

                final String repl;
                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);
                        }
                    }
                    case 'c', 'd', 'f', 's' -> {
                        if (j == args.length) {
                            throw new IllegalArgumentException("Too few arguments");
                        }
                        final Object arg = args[j++];
                        repl = String.valueOf(arg);
                    }
                    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());
                        }
                    }
                    case '%' -> {
                        repl = "%";
                    }
                    default -> {
                        throw new IllegalArgumentException("Unsupported format code " + code);
                    }
                }

                sb.replace(i, i + consume, repl);

                // Apply any argument width request
                final int prefixInsert = (prefixChar == '0' && repl.charAt(0) == '-') ? 1 : 0;
                for (int k = repl.length(); k < prefixLen; k++) {
                    sb.insert(i + prefixInsert, prefixChar);
                }
                i += Math.max(repl.length(), prefixLen);
            } else {
                i++;
            }
        }
        if (j != args.length) {
            throw new IllegalArgumentException("Too many arguments");
        }
        return sb.toString();
    }
}
+5 −3
Original line number Diff line number Diff line
@@ -629,7 +629,8 @@ public class MediaPlayerList {
                int id = mMediaPlayerIds.get(browser.getPackageName());

                Log.d(TAG, "getFolderItemsMediaPlayerList: Adding player " + displayName);
                Folder playerFolder = new Folder(String.format("%02d", id), false, displayName);
                Folder playerFolder =
                        new Folder(Utils.formatSimple("%02d", id), false, displayName);
                playerList.add(new ListItem(playerFolder));
            }
        } else {
@@ -639,7 +640,8 @@ public class MediaPlayerList {
                int id = mMediaPlayerIds.get(player.getPackageName());

                Log.d(TAG, "getFolderItemsMediaPlayerList: Adding player " + displayName);
                Folder playerFolder = new Folder(String.format("%02d", id), false, displayName);
                Folder playerFolder =
                        new Folder(Utils.formatSimple("%02d", id), false, displayName);
                playerList.add(new ListItem(playerFolder));
            }
        }
@@ -722,7 +724,7 @@ public class MediaPlayerList {
                                return;
                            }

                            String playerPrefix = String.format("%02d", playerIndex);
                            String playerPrefix = Utils.formatSimple("%02d", playerIndex);
                            for (ListItem item : results) {
                                if (item.isFolder) {
                                    item.folder.mediaId = playerPrefix.concat(item.folder.mediaId);
+4 −5
Original line number Diff line number Diff line
@@ -16,9 +16,10 @@

package com.android.bluetooth.avrcpcontroller;

import com.android.bluetooth.Utils;

import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import java.util.regex.Matcher;
@@ -134,8 +135,7 @@ public class BipDateTime {
            TimeZone utc = TimeZone.getTimeZone("UTC");
            utc.setRawOffset(0);
            cal.setTimeZone(utc);
            return String.format(
                    Locale.US,
            return Utils.formatSimple(
                    "%04d%02d%02dT%02d%02d%02dZ",
                    cal.get(Calendar.YEAR),
                    cal.get(Calendar.MONTH) + 1,
@@ -145,8 +145,7 @@ public class BipDateTime {
                    cal.get(Calendar.SECOND));
        } else {
            cal.setTimeZone(TimeZone.getDefault());
            return String.format(
                    Locale.US,
            return Utils.formatSimple(
                    "%04d%02d%02dT%02d%02d%02d",
                    cal.get(Calendar.YEAR),
                    cal.get(Calendar.MONTH) + 1,
+4 −13
Original line number Diff line number Diff line
@@ -2011,11 +2011,7 @@ public class AdapterService extends Service {
    private int startRfcommListener(
            String name, ParcelUuid uuid, PendingIntent pendingIntent, AttributionSource source) {
        if (mBluetoothServerSockets.containsKey(uuid.getUuid())) {
            Log.d(
                    TAG,
                    String.format(
                            "Cannot start RFCOMM listener: UUID %s already in use.",
                            uuid.getUuid()));
            Log.d(TAG, "Cannot start RFCOMM listener: UUID " + uuid.getUuid() + "already in use.");
            return BluetoothStatusCodes.RFCOMM_LISTENER_START_FAILED_UUID_IN_USE;
        }

@@ -2034,11 +2030,7 @@ public class AdapterService extends Service {
        RfcommListenerData listenerData = mBluetoothServerSockets.get(uuid.getUuid());

        if (listenerData == null) {
            Log.d(
                    TAG,
                    String.format(
                            "Cannot stop RFCOMM listener: UUID %s is not registered.",
                            uuid.getUuid()));
            Log.d(TAG, "Cannot stop RFCOMM listener: UUID " + uuid.getUuid() + "is not registered");
            return BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_NO_MATCHING_SERVICE_RECORD;
        }

@@ -6208,7 +6200,7 @@ public class AdapterService extends Service {
                        + (" rxTime = " + rxTime)
                        + (" idleTime = " + idleTime)
                        + (" energyUsed = " + energyUsed)
                        + (" ctrlState = " + String.format("0x%08x", ctrlState))
                        + (" ctrlState = " + Utils.formatSimple("0x%08x", ctrlState))
                        + (" traffic = " + Arrays.toString(data)));
    }

@@ -6377,8 +6369,7 @@ public class AdapterService extends Service {
        ArrayList<String> initFlags = new ArrayList<>();
        for (String property : properties.getKeyset()) {
            if (property.startsWith("INIT_")) {
                initFlags.add(
                        String.format("%s=%s", property, properties.getString(property, null)));
                initFlags.add(property + "=" + properties.getString(property, null));
            }
        }
        return initFlags.toArray(new String[0]);
+1 −5
Original line number Diff line number Diff line
@@ -249,11 +249,7 @@ public class CompanionManager {
                @Override
                public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
                    String valueStr = new String(value);
                    Log.d(
                            TAG,
                            String.format(
                                    "Metadata updated in Device %s: %d = %s.",
                                    device, key, value == null ? null : valueStr));
                    Log.d(TAG, "Metadata updated in " + device + ": " + key + "=" + valueStr);
                    if (key == BluetoothDevice.METADATA_SOFTWARE_VERSION
                            && (valueStr.equals(BluetoothDevice.COMPANION_TYPE_PRIMARY)
                                    || valueStr.equals(BluetoothDevice.COMPANION_TYPE_SECONDARY))) {
Loading