ByteUtils: replace Guava comparator with arrayUnsignedComparator()

Replace Guava UnsignedBytes.lexicographicComparator() with
ByteUtils.arrayUnsignedComparator().
This commit is contained in:
Sean Gilligan 2023-03-02 11:53:25 -08:00 committed by Andreas Schildbach
parent 17abffa04b
commit 50caafc8d5
5 changed files with 70 additions and 8 deletions

View file

@ -18,8 +18,8 @@
package org.bitcoinj.base;
import com.google.common.primitives.UnsignedBytes;
import org.bitcoinj.base.exceptions.AddressFormatException;
import org.bitcoinj.base.utils.ByteUtils;
import org.bitcoinj.crypto.ECKey;
import org.bitcoinj.core.NetworkParameters;
@ -270,8 +270,8 @@ public class LegacyAddress extends Address {
// Comparator for LegacyAddress, left argument must be LegacyAddress, right argument can be any Address
private static final Comparator<Address> LEGACY_ADDRESS_COMPARATOR = Address.PARTIAL_ADDRESS_COMPARATOR
.thenComparingInt(a -> ((LegacyAddress) a).getVersion()) // Then compare Legacy address version byte
.thenComparing(a -> a.bytes, UnsignedBytes.lexicographicalComparator()); // Then compare Legacy bytes
.thenComparingInt(a -> ((LegacyAddress) a).getVersion()) // Then compare Legacy address version byte
.thenComparing(a -> a.bytes, ByteUtils.arrayUnsignedComparator()); // Then compare Legacy bytes
/**
* {@inheritDoc}

View file

@ -16,8 +16,8 @@
package org.bitcoinj.base;
import com.google.common.primitives.UnsignedBytes;
import org.bitcoinj.base.exceptions.AddressFormatException;
import org.bitcoinj.base.utils.ByteUtils;
import org.bitcoinj.crypto.ECKey;
import org.bitcoinj.core.NetworkParameters;
@ -426,7 +426,7 @@ public class SegwitAddress extends Address {
// Comparator for SegwitAddress, left argument must be SegwitAddress, right argument can be any Address
private static final Comparator<Address> SEGWIT_ADDRESS_COMPARATOR = Address.PARTIAL_ADDRESS_COMPARATOR
.thenComparing(a -> a.bytes, UnsignedBytes.lexicographicalComparator()); // Then compare Segwit bytes
.thenComparing(a -> a.bytes, ByteUtils.arrayUnsignedComparator()); // Then compare Segwit bytes
/**
* {@inheritDoc}

View file

@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Comparator;
import static com.google.common.base.Preconditions.checkArgument;
@ -373,4 +374,28 @@ public class ByteUtils {
public static void setBitLE(byte[] data, int index) {
data[index >>> 3] |= bitMask[7 & index];
}
/**
* Provides a byte array comparator.
* @return A comparator for byte[]
*/
public static Comparator<byte[]> arrayUnsignedComparator() {
return ARRAY_UNSIGNED_COMPARATOR;
}
// In Java 9, this can be replaced with Arrays.compareUnsigned()
private static final Comparator<byte[]> ARRAY_UNSIGNED_COMPARATOR = (a, b) -> {
int minLength = Math.min(a.length, b.length);
for (int i = 0; i < minLength; i++) {
int result = compareUnsigned(a[i], b[i]);
if (result != 0) {
return result;
}
}
return a.length - b.length;
};
private static int compareUnsigned(byte a, byte b) {
return Byte.toUnsignedInt(a) - Byte.toUnsignedInt(b);
}
}

View file

@ -20,7 +20,6 @@ package org.bitcoinj.crypto;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedBytes;
import org.bitcoin.NativeSecp256k1;
import org.bitcoin.NativeSecp256k1Util;
import org.bitcoin.Secp256k1Context;
@ -118,8 +117,8 @@ import static com.google.common.base.Preconditions.checkState;
*/
public class ECKey implements EncryptableItem {
private static final Logger log = LoggerFactory.getLogger(ECKey.class);
// Note: this can be replaced with Arrays.compare(a, b) once we require Java 9
private static final Comparator<byte[]> LEXICOGRAPHICAL_COMPARATOR = UnsignedBytes.lexicographicalComparator();
// Note: this can be replaced with Arrays.compareUnsigned(a, b) once we require Java 9
private static final Comparator<byte[]> LEXICOGRAPHICAL_COMPARATOR = ByteUtils.arrayUnsignedComparator();
/** Sorts oldest keys first, newest last. */
public static final Comparator<ECKey> AGE_COMPARATOR = Comparator.comparingLong(k -> k.creationTimeSeconds);

View file

@ -22,6 +22,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
@ -86,6 +87,43 @@ public class ByteUtilsTest {
};
}
@Test
@Parameters(method = "arrayUnsignedComparatorVectors")
public void testArrayUnsignedComparator(String stringA, String stringB, int expectedResult) {
Comparator<byte[]> comparator = ByteUtils.arrayUnsignedComparator();
byte[] a = ByteUtils.parseHex(stringA);
byte[] b = ByteUtils.parseHex(stringB);
int actual = comparator.compare(a, b);
assertEquals("", expectedResult, Integer.signum(actual));
}
private Object[] arrayUnsignedComparatorVectors() {
return new Object[]{
new Object[]{ "00", "00", 0},
new Object[]{ "FF", "FF", 0},
new Object[]{ "00", "01", -1},
new Object[]{ "80", "81", -1},
new Object[]{ "FE", "FF", -1},
new Object[]{ "01", "00", 1},
new Object[]{ "81", "80", 1},
new Object[]{ "FF", "FE", 1},
new Object[]{ "00", "0001", -1},
new Object[]{ "FF", "FF00", -1},
new Object[]{ "0001", "00", 1},
new Object[]{ "FF00", "FF", 1},
new Object[]{ "000102030405060708090A", "000102030405060708090A", 0},
new Object[]{ "FF0102030405060708090A", "000102030405060708090A", 1},
new Object[]{ "000102030405060708090A", "FF0102030405060708090A", -1},
new Object[]{ "0001", "000102030405060708090A", -1},
new Object[]{ "FF01", "000102030405060708090A", 1},
new Object[]{ "0001", "FF0102030405060708090A", -1},
new Object[]{ "", "", 0}
};
}
@Test
public void testReverseBytes() {
assertArrayEquals(new byte[]{1, 2, 3, 4, 5}, ByteUtils.reverseBytes(new byte[]{5, 4, 3, 2, 1}));