Skip to content

Commit 44321d3

Browse files
committed
Add unsigned sort transformer
fixes #17
1 parent cb383cd commit 44321d3

6 files changed

Lines changed: 72 additions & 35 deletions

File tree

CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* add `startsWith()` and `endsWidth()` methods #12
1212
* add cache for calculating the hashCode
1313
* add HMAC byte transformer #11
14+
* add unsigned sort transformer #17
1415

1516
## v0.6.0
1617

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ Bytes decompressed = compressed.transform(decompressGzip());
271271
**Sorting** of individual bytes with either [`Comparator`](https://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html) or natural order:
272272

273273
```java
274-
Bytes.wrap(array).transform(sort());
274+
Bytes.wrap(array).transform(sort()); // 0x00 sorts after 0xff
275+
Bytes.wrap(array).transform(sortUnsigned()); // 0xff sorts after 0x00
275276
Bytes.wrap(array).transform(sort(byteComparator));
276277
```
277278

src/main/java/at/favre/lib/bytes/Bytes.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@
2121

2222
package at.favre.lib.bytes;
2323

24-
import java.io.ByteArrayInputStream;
25-
import java.io.DataInput;
26-
import java.io.File;
27-
import java.io.InputStream;
28-
import java.io.Serializable;
24+
import java.io.*;
2925
import java.math.BigInteger;
3026
import java.nio.ByteBuffer;
3127
import java.nio.ByteOrder;
@@ -35,14 +31,7 @@
3531
import java.nio.charset.StandardCharsets;
3632
import java.security.SecureRandom;
3733
import java.text.Normalizer;
38-
import java.util.Arrays;
39-
import java.util.BitSet;
40-
import java.util.Collection;
41-
import java.util.Iterator;
42-
import java.util.List;
43-
import java.util.Objects;
44-
import java.util.Random;
45-
import java.util.UUID;
34+
import java.util.*;
4635

4736
/**
4837
* Bytes is wrapper class for an byte-array that allows a lot of convenience operations on it:
@@ -67,6 +56,10 @@
6756
* b.not();
6857
* System.out.println(b.encodeHex());
6958
* </pre>
59+
*
60+
* <h3>Comparable</h3>
61+
* The implemented comparator treats the bytes as signed bytes. If you want to sort, treating each byte as unsigned,
62+
* use {@link BytesTransformers#sortUnsigned()}.
7063
*/
7164
@SuppressWarnings("WeakerAccess")
7265
public class Bytes implements Comparable<Bytes>, Serializable, Iterable<Byte> {

src/main/java/at/favre/lib/bytes/BytesTransformers.java

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,34 @@ public static BytesTransformer shuffle(Random random) {
4444
}
4545

4646
/**
47-
* Create a {@link BytesTransformer} which sorts the internal byte array with it's natural ordering.
47+
* Create a {@link BytesTransformer} which sorts the internal byte array with it's natural ordering treating
48+
* each byte as signed byte (-128...127). Using inplace sorting, this can be reasonable fast.
4849
*
4950
* @return transformer
5051
*/
5152
public static BytesTransformer sort() {
5253
return new SortTransformer();
5354
}
5455

56+
/**
57+
* Create a {@link BytesTransformer} which sorts the internal byte array with it's natural ordering treating
58+
* each byte as unsigned byte (0...255). That is, the byte string {@code ff} sorts after {@code 00}.
59+
*
60+
* <strong>Note:</strong> this requires 2 copies of the internal array and a lot of unboxing due to
61+
* the fact that no primitives are not allowed as generic type arguments - so only use on small arrays.
62+
*
63+
* @return transformer
64+
*/
65+
public static BytesTransformer sortUnsigned() {
66+
return new SortTransformer(new SortTransformer.UnsignedByteComparator());
67+
}
68+
5569
/**
5670
* Create a {@link BytesTransformer} which sorts the internal byte array according to given comparator.
5771
*
72+
* <strong>Note:</strong> this requires 2 copies of the internal array and a lot of unboxing due to
73+
* the fact that no primitives are not allowed as generic type arguments - so only use on small arrays.
74+
*
5875
* @param comparator to sort the bytes
5976
* @return transformer
6077
*/
@@ -168,6 +185,7 @@ public byte[] transform(byte[] currentArray, boolean inPlace) {
168185
public boolean supportInPlaceTransformation() {
169186
return true;
170187
}
188+
171189
}
172190

173191
/**
@@ -202,26 +220,26 @@ public byte[] transform(byte[] currentArray, boolean inPlace) {
202220
public boolean supportInPlaceTransformation() {
203221
return comparator == null;
204222
}
223+
224+
/**
225+
* Converting each byte into unsinged version and comparing it (0...255) vs (-128..127)
226+
*/
227+
static final class UnsignedByteComparator implements Comparator<Byte> {
228+
@Override
229+
public int compare(Byte o1, Byte o2) {
230+
int byteA = o1 & 0xff;
231+
int byteB = o2 & 0xff;
232+
if (byteA == byteB) return 0;
233+
return byteA < byteB ? -1 : 1;
234+
}
235+
}
236+
205237
}
206238

207239
/**
208240
* Adds or converts to arbitrary checksum
209241
*/
210242
public static final class ChecksumTransformer implements BytesTransformer {
211-
/**
212-
* Definitions of the mode
213-
*/
214-
public enum Mode {
215-
/**
216-
* Appends checksum to given byte array
217-
*/
218-
APPEND,
219-
/**
220-
* Transforms byte array and returns only checksum
221-
*/
222-
TRANSFORM
223-
}
224-
225243
private final Checksum checksum;
226244
private final Mode mode;
227245
private final int checksumLengthByte;
@@ -252,6 +270,20 @@ public byte[] transform(byte[] currentArray, boolean inPlace) {
252270
public boolean supportInPlaceTransformation() {
253271
return false;
254272
}
273+
274+
/**
275+
* Definitions of the mode
276+
*/
277+
public enum Mode {
278+
/**
279+
* Appends checksum to given byte array
280+
*/
281+
APPEND,
282+
/**
283+
* Transforms byte array and returns only checksum
284+
*/
285+
TRANSFORM
286+
}
255287
}
256288

257289
/**

src/main/java/at/favre/lib/bytes/Util.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,8 @@ static ByteBuffer getBytesFromUUID(UUID uuid) {
497497
/**
498498
* Hashcode implementation for a byte array and given byte order
499499
*
500-
* @param byteArray
501-
* @param byteOrder
500+
* @param byteArray to calculate hashCode of
501+
* @param byteOrder to calculate hashCode of
502502
* @return hashCode
503503
*/
504504
static int hashCode(byte[] byteArray, ByteOrder byteOrder) {

src/test/java/at/favre/lib/bytes/BytesTransformTest.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,16 +270,26 @@ public void shuffleTest() {
270270
}
271271

272272
@Test
273-
public void sortTest() {
274-
byte[] sorted = new byte[]{0, 1, 2, 3, 4, 5, 6};
273+
public void sortSignedTest() {
274+
byte[] sorted = new byte[]{-2, -1, 0, 1, 2, 3, 4, 5, 6};
275275
assertArrayEquals(sorted, Bytes.from(sorted).transform(shuffle()).transform(sort()).array());
276-
assertArrayEquals(sorted, Bytes.from(new byte[]{6, 0, 3, 4, 1, 5, 2}).transform(sort()).array());
277-
assertArrayEquals(Bytes.from(sorted).reverse().array(), Bytes.from(new byte[]{6, 0, 3, 4, 1, 5, 2}).transform(sort(new Comparator<Byte>() {
276+
assertArrayEquals(sorted, Bytes.from(new byte[]{6, 0, 3, -2, -1, 4, 1, 5, 2}).transform(sort()).array());
277+
assertArrayEquals(Bytes.from(sorted).reverse().array(), Bytes.from(new byte[]{6, -2, -1, 0, 3, 4, 1, 5, 2}).transform(sort(new Comparator<Byte>() {
278278
@Override
279279
public int compare(Byte o1, Byte o2) {
280280
return o2.compareTo(o1);
281281
}
282282
})).array());
283+
284+
byte[] checkSignedSorted = new byte[]{(byte) 0x80, (byte) 0xFE, (byte) 0xFF, 0x00, 0x01};
285+
assertArrayEquals(checkSignedSorted, Bytes.from(checkSignedSorted).transform(shuffle()).transform(sort()).array());
286+
}
287+
288+
@Test
289+
public void sortUnsignedTest() {
290+
byte[] sorted = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, (byte) 0x80, (byte) 0xAE, (byte) 0xFF};
291+
assertArrayEquals(sorted, Bytes.from(sorted).transform(shuffle()).transform(sortUnsigned()).array());
292+
assertArrayEquals(sorted, Bytes.from(new byte[]{(byte) 0x80, (byte) 0xAE, (byte) 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}).transform(sortUnsigned()).array());
283293
}
284294

285295
@Test

0 commit comments

Comments
 (0)