Reduce unecessary allocation churn in MdnsServiceTypeClient.processResponse

Bug: 413371501
Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.connectivity.mdns.MdnsServiceTypeClientTests
Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.net.module.util.CollectionUtilsTest
Flag: EXEMPT bugfix

Change-Id: Id9abf0b79f6db155e6895b0dd7661d0aafb6b0a7
This commit is contained in:
Richard Uhler 2025-06-18 11:19:40 -07:00
commit 532b263b45
6 changed files with 60 additions and 6 deletions

View file

@ -298,6 +298,11 @@ public class MdnsResponse {
return new LinkedList<>(records);
}
/** Gets the number of records. */
synchronized int getNumRecords() {
return records.size();
}
/**
* Drop address records if they are for a hostname that does not match the service record.
*

View file

@ -265,7 +265,7 @@ public class MdnsResponseDecoder {
for (int i = 0; i < augmentedToOriginal.size(); i++) {
final MdnsResponse augmented = augmentedToOriginal.keyAt(i);
final MdnsResponse original = augmentedToOriginal.valueAt(i);
if (augmented.getRecords().size() != original.getRecords().size()) {
if (augmented.getNumRecords() != original.getNumRecords()) {
modified.add(augmented);
}
}
@ -304,4 +304,4 @@ public class MdnsResponseDecoder {
}
return result == null ? List.of() : result;
}
}
}

View file

@ -59,14 +59,22 @@ public class MdnsServiceCache {
public static class CacheKey {
@NonNull final String mUpperCaseServiceType;
@NonNull final SocketKey mSocketKey;
private final int mHashCode;
CacheKey(@NonNull String serviceType, @NonNull SocketKey socketKey) {
mUpperCaseServiceType = toDnsUpperCase(serviceType);
mSocketKey = socketKey;
// Equivalent to Objects.hash(mUpperCaseServiceType, mSocketKey),
// but without the unnecessary array allocation.
int hashCode = 1;
hashCode = 31 * hashCode + mUpperCaseServiceType.hashCode();
hashCode = 31 * hashCode + mSocketKey.hashCode();
mHashCode = hashCode;
}
@Override public int hashCode() {
return Objects.hash(mUpperCaseServiceType, mSocketKey);
return mHashCode;
}
@Override public boolean equals(Object other) {
@ -180,7 +188,8 @@ public class MdnsServiceCache {
*/
public static MdnsResponse findMatchedResponse(@NonNull List<MdnsResponse> responses,
@NonNull String serviceName) {
for (MdnsResponse response : responses) {
for (int i = 0; i < responses.size(); ++i) {
MdnsResponse response = responses.get(i);
if (equalsIgnoreDnsCase(serviceName, response.getServiceInstanceName())) {
return response;
}
@ -190,7 +199,8 @@ public class MdnsServiceCache {
private static CachedService findMatchedCachedService(
@NonNull List<CachedService> cachedServices, @NonNull String serviceName) {
for (CachedService cachedService : cachedServices) {
for (int i = 0; i < cachedServices.size(); ++i) {
CachedService cachedService = cachedServices.get(i);
if (equalsIgnoreDnsCase(serviceName, cachedService.mService.getServiceInstanceName())) {
return cachedService;
}

View file

@ -40,6 +40,7 @@ public class SocketKey {
*/
@NonNull
private final String mInterfaceName;
private final int mHashCode;
SocketKey(int interfaceIndex, @NonNull String interfaceName) {
this(null /* network */, interfaceIndex, interfaceName);
@ -49,6 +50,13 @@ public class SocketKey {
mNetwork = network;
mInterfaceIndex = interfaceIndex;
mInterfaceName = interfaceName;
// Equivalent to Objects.hash(mNetwork, mInterfaceIndex), but without
// the unnecessary array allocation.
int hashCode = 1;
hashCode = 31 * hashCode + (mNetwork == null ? 0 : mNetwork.hashCode());
hashCode = 31 * hashCode + Integer.hashCode(mInterfaceIndex);
mHashCode = hashCode;
}
@Nullable
@ -67,7 +75,7 @@ public class SocketKey {
@Override
public int hashCode() {
return Objects.hash(mNetwork, mInterfaceIndex);
return mHashCode;
}
@Override

View file

@ -113,6 +113,16 @@ public final class CollectionUtils {
return indexOf(elem, predicate) >= 0;
}
/**
* @return True if any element satisfies the predicate, false otherwise.
* Note that means this always returns false for empty collections.
*
* This is an overload for List that avoids unnecessary iterator allocation.
*/
public static <T> boolean any(@NonNull List<T> elem, @NonNull Predicate<T> predicate) {
return indexOf(elem, predicate) >= 0;
}
/**
* @return The index of the first element that matches the predicate, or -1 if none.
*/
@ -126,6 +136,21 @@ public final class CollectionUtils {
return -1;
}
/**
* @return The index of the first element that matches the predicate, or -1 if none.
*
* This is an overload for List that avoids unnecessary iterator allocation.
*/
public static <T> int indexOf(@NonNull final List<T> elem,
@NonNull final Predicate<? super T> predicate) {
for (int idx = 0; idx < elem.size(); ++idx) {
if (predicate.test(elem.get(idx))) {
return idx;
}
}
return -1;
}
/**
* @return True if there exists at least one element in the sparse array for which
* condition {@code predicate}

View file

@ -53,6 +53,12 @@ class CollectionUtilsTest {
assertEquals(1, CollectionUtils.indexOf(listOf("AA", "BBB", "CCCC")) { it.length >= 3 })
assertEquals(1, CollectionUtils.indexOf(listOf("AA", null, "CCCC")) { it == null })
assertEquals(1, CollectionUtils.indexOf(listOf(null, "CCCC")) { it != null })
assertEquals(4, CollectionUtils.indexOf(setOf("A", "B", "C", "D", "E")) { it == "E" })
assertEquals(0, CollectionUtils.indexOf(setOf("A", "B", "C", "D", "E")) { it == "A" })
assertEquals(1, CollectionUtils.indexOf(setOf("AA", "BBB", "CCCC")) { it.length >= 3 })
assertEquals(1, CollectionUtils.indexOf(setOf("AA", null, "CCCC")) { it == null })
assertEquals(1, CollectionUtils.indexOf(setOf(null, "CCCC")) { it != null })
}
@Test