From c2ef5bdba6e0eb9fd9eb6ed4fdae75727cc36783 Mon Sep 17 00:00:00 2001 From: pfavre Date: Sun, 29 Nov 2020 19:55:00 +0100 Subject: [PATCH 01/39] Update Links to Travis CI (using .com now as required by Travis migration) --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 389643f..fb4a105 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ to blindly paste code snippets from [m](https://stackoverflow.com/questions/1519736/random-shuffling-of-an-array) [![Download](https://api.bintray.com/packages/patrickfav/maven/bytes-java/images/download.svg)](https://bintray.com/patrickfav/maven/bytes-java/_latestVersion) -[![Build Status](https://travis-ci.org/patrickfav/bytes-java.svg?branch=master)](https://travis-ci.org/patrickfav/bytes-java) +[![Build Status](https://travis-ci.com/patrickfav/bytes-java.svg?branch=master)](https://travis-ci.com/patrickfav/bytes-java) [![Javadocs](https://www.javadoc.io/badge/at.favre.lib/bytes.svg)](https://www.javadoc.io/doc/at.favre.lib/bytes) [![Coverage Status](https://coveralls.io/repos/github/patrickfav/bytes-java/badge.svg?branch=master)](https://coveralls.io/github/patrickfav/bytes-java?branch=master) [![Maintainability](https://api.codeclimate.com/v1/badges/43b7770f0ee00b85f92a/maintainability)](https://codeclimate.com/github/patrickfav/bytes-java/maintainability) diff --git a/pom.xml b/pom.xml index 8d70d20..0a27b42 100644 --- a/pom.xml +++ b/pom.xml @@ -138,6 +138,6 @@ Travis - https://travis-ci.org/patrickfav/bytes-java + https://travis-ci.com/patrickfav/bytes-java From 2208aade7db2f116543940f0ef13f80dc302b02f Mon Sep 17 00:00:00 2001 From: Grant Peltier Date: Sun, 21 Feb 2021 11:40:48 -0600 Subject: [PATCH 02/39] Fix Bytes.bitAt to respect byte order Closes #39. bitAt now checks byte order before locating the desired bit. --- src/main/java/at/favre/lib/bytes/Bytes.java | 6 +++++- src/test/java/at/favre/lib/bytes/BytesMiscTest.java | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index b033e41..a04e319 100644 --- a/src/main/java/at/favre/lib/bytes/Bytes.java +++ b/src/main/java/at/favre/lib/bytes/Bytes.java @@ -1338,7 +1338,11 @@ public boolean endsWith(byte[] subArray) { */ public boolean bitAt(int bitIndex) { Util.Validation.checkIndexBounds(lengthBit(), bitIndex, 1, "bit"); - return ((byteAt(length() - 1 - (bitIndex / 8)) >>> bitIndex % 8) & 1) != 0; + if (byteOrder == ByteOrder.BIG_ENDIAN) { + return ((byteAt(length() - 1 - (bitIndex / 8)) >>> bitIndex % 8) & 1) != 0; + } else { + return ((byteAt(bitIndex / 8) >>> bitIndex % 8) & 1) != 0; + } } /** diff --git a/src/test/java/at/favre/lib/bytes/BytesMiscTest.java b/src/test/java/at/favre/lib/bytes/BytesMiscTest.java index 95303f7..ed832bb 100644 --- a/src/test/java/at/favre/lib/bytes/BytesMiscTest.java +++ b/src/test/java/at/favre/lib/bytes/BytesMiscTest.java @@ -272,6 +272,12 @@ public void bitAt() { fail(); } catch (IndexOutOfBoundsException ignored) { } + + Bytes bytes = Bytes.wrap(new byte[]{1, 0, 2, 0}).byteOrder(ByteOrder.LITTLE_ENDIAN); + assertTrue(bytes.bitAt(0)); + assertTrue(bytes.bitAt(17)); + assertFalse(bytes.bitAt(8)); + assertFalse(bytes.bitAt(31)); } @Test From e997250f181cf60d3dbfb729a4c755e06fdf14d3 Mon Sep 17 00:00:00 2001 From: Grant Peltier Date: Mon, 22 Feb 2021 16:56:27 -0600 Subject: [PATCH 03/39] Fix bit shift methods to respect byte order Closes #41. Bytes.leftShift and Bytes.rightShift now behave as expected based on the set byte order. --- src/main/java/at/favre/lib/bytes/Bytes.java | 12 ++- .../at/favre/lib/bytes/BytesTransformer.java | 9 ++- src/main/java/at/favre/lib/bytes/Util.java | 78 +++++++++++++------ .../favre/lib/bytes/BytesTransformTest.java | 3 +- .../java/at/favre/lib/bytes/UtilByteTest.java | 73 ++++++++++------- 5 files changed, 120 insertions(+), 55 deletions(-) diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index a04e319..f1ab738 100644 --- a/src/main/java/at/favre/lib/bytes/Bytes.java +++ b/src/main/java/at/favre/lib/bytes/Bytes.java @@ -967,7 +967,11 @@ public Bytes not() { * @see Bit shifts */ public Bytes leftShift(int shiftCount) { - return transform(new BytesTransformer.ShiftTransformer(shiftCount, BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT)); + return transform(new BytesTransformer.ShiftTransformer( + shiftCount, + BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT, + byteOrder + )); } /** @@ -982,7 +986,11 @@ public Bytes leftShift(int shiftCount) { * @see Bit shifts */ public Bytes rightShift(int shiftCount) { - return transform(new BytesTransformer.ShiftTransformer(shiftCount, BytesTransformer.ShiftTransformer.Type.RIGHT_SHIFT)); + return transform(new BytesTransformer.ShiftTransformer( + shiftCount, + BytesTransformer.ShiftTransformer.Type.RIGHT_SHIFT, + byteOrder + )); } /** diff --git a/src/main/java/at/favre/lib/bytes/BytesTransformer.java b/src/main/java/at/favre/lib/bytes/BytesTransformer.java index 3612505..22367d8 100644 --- a/src/main/java/at/favre/lib/bytes/BytesTransformer.java +++ b/src/main/java/at/favre/lib/bytes/BytesTransformer.java @@ -21,6 +21,7 @@ package at.favre.lib.bytes; +import java.nio.ByteOrder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Objects; @@ -131,10 +132,12 @@ public enum Type { private final int shiftCount; private final Type type; + private final ByteOrder byteOrder; - ShiftTransformer(int shiftCount, Type type) { + ShiftTransformer(int shiftCount, Type type, ByteOrder byteOrder) { this.shiftCount = shiftCount; this.type = Objects.requireNonNull(type, "passed shift type must not be null"); + this.byteOrder = Objects.requireNonNull(byteOrder, "passed byteOrder type must not be null"); } @Override @@ -143,10 +146,10 @@ public byte[] transform(byte[] currentArray, boolean inPlace) { switch (type) { case RIGHT_SHIFT: - return Util.Byte.shiftRight(out, shiftCount); + return Util.Byte.shiftRight(out, shiftCount, byteOrder); default: case LEFT_SHIFT: - return Util.Byte.shiftLeft(out, shiftCount); + return Util.Byte.shiftLeft(out, shiftCount, byteOrder); } } diff --git a/src/main/java/at/favre/lib/bytes/Util.java b/src/main/java/at/favre/lib/bytes/Util.java index 2588223..ee494fc 100644 --- a/src/main/java/at/favre/lib/bytes/Util.java +++ b/src/main/java/at/favre/lib/bytes/Util.java @@ -291,25 +291,42 @@ static void reverse(byte[] array, int fromIndex, int toIndex) { * * @param byteArray to shift * @param shiftBitCount how many bits to shift + * @param byteOrder endianness of given byte array * @return shifted byte array */ - static byte[] shiftLeft(byte[] byteArray, int shiftBitCount) { + static byte[] shiftLeft(byte[] byteArray, int shiftBitCount, ByteOrder byteOrder) { final int shiftMod = shiftBitCount % 8; final byte carryMask = (byte) ((1 << shiftMod) - 1); final int offsetBytes = (shiftBitCount / 8); int sourceIndex; - for (int i = 0; i < byteArray.length; i++) { - sourceIndex = i + offsetBytes; - if (sourceIndex >= byteArray.length) { - byteArray[i] = 0; - } else { - byte src = byteArray[sourceIndex]; - byte dst = (byte) (src << shiftMod); - if (sourceIndex + 1 < byteArray.length) { - dst |= byteArray[sourceIndex + 1] >>> (8 - shiftMod) & carryMask; + if (byteOrder == ByteOrder.BIG_ENDIAN) { + for (int i = 0; i < byteArray.length; i++) { + sourceIndex = i + offsetBytes; + if (sourceIndex >= byteArray.length) { + byteArray[i] = 0; + } else { + byte src = byteArray[sourceIndex]; + byte dst = (byte) (src << shiftMod); + if (sourceIndex + 1 < byteArray.length) { + dst |= byteArray[sourceIndex + 1] >>> (8 - shiftMod) & carryMask; + } + byteArray[i] = dst; + } + } + } else { + for (int i = byteArray.length - 1; i >= 0; i--) { + sourceIndex = i - offsetBytes; + if (sourceIndex < 0) { + byteArray[i] = 0; + } else { + byte src = byteArray[sourceIndex]; + byte dst = (byte) (src << shiftMod); + if (sourceIndex - 1 >= 0) { + dst |= byteArray[sourceIndex - 1] >>> (8 - shiftMod) & carryMask; + } + byteArray[i] = dst; } - byteArray[i] = dst; } } return byteArray; @@ -330,25 +347,42 @@ static byte[] shiftLeft(byte[] byteArray, int shiftBitCount) { * * @param byteArray to shift * @param shiftBitCount how many bits to shift + * @param byteOrder endianness of given byte array * @return shifted byte array */ - static byte[] shiftRight(byte[] byteArray, int shiftBitCount) { + static byte[] shiftRight(byte[] byteArray, int shiftBitCount, ByteOrder byteOrder) { final int shiftMod = shiftBitCount % 8; final byte carryMask = (byte) (0xFF << (8 - shiftMod)); final int offsetBytes = (shiftBitCount / 8); int sourceIndex; - for (int i = byteArray.length - 1; i >= 0; i--) { - sourceIndex = i - offsetBytes; - if (sourceIndex < 0) { - byteArray[i] = 0; - } else { - byte src = byteArray[sourceIndex]; - byte dst = (byte) ((0xff & src) >>> shiftMod); - if (sourceIndex - 1 >= 0) { - dst |= byteArray[sourceIndex - 1] << (8 - shiftMod) & carryMask; + if (byteOrder == ByteOrder.BIG_ENDIAN) { + for (int i = byteArray.length - 1; i >= 0; i--) { + sourceIndex = i - offsetBytes; + if (sourceIndex < 0) { + byteArray[i] = 0; + } else { + byte src = byteArray[sourceIndex]; + byte dst = (byte) ((0xff & src) >>> shiftMod); + if (sourceIndex - 1 >= 0) { + dst |= byteArray[sourceIndex - 1] << (8 - shiftMod) & carryMask; + } + byteArray[i] = dst; + } + } + } else { + for (int i = 0; i < byteArray.length; i++) { + sourceIndex = i + offsetBytes; + if (sourceIndex >= byteArray.length) { + byteArray[i] = 0; + } else { + byte src = byteArray[sourceIndex]; + byte dst = (byte) ((0xff & src) >>> shiftMod); + if (sourceIndex + 1 < byteArray.length) { + dst |= byteArray[sourceIndex + 1] << (8 - shiftMod) & carryMask; + } + byteArray[i] = dst; } - byteArray[i] = dst; } } return byteArray; diff --git a/src/test/java/at/favre/lib/bytes/BytesTransformTest.java b/src/test/java/at/favre/lib/bytes/BytesTransformTest.java index dd6feaf..8041380 100644 --- a/src/test/java/at/favre/lib/bytes/BytesTransformTest.java +++ b/src/test/java/at/favre/lib/bytes/BytesTransformTest.java @@ -25,6 +25,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.Comparator; @@ -446,7 +447,7 @@ public void transformerInPlaceTest() { assertTrue(new BytesTransformer.BitSwitchTransformer(0, true).supportInPlaceTransformation()); assertTrue(new BytesTransformer.BitWiseOperatorTransformer(new byte[]{}, BytesTransformer.BitWiseOperatorTransformer.Mode.XOR).supportInPlaceTransformation()); assertTrue(new BytesTransformer.NegateTransformer().supportInPlaceTransformation()); - assertTrue(new BytesTransformer.ShiftTransformer(0, BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT).supportInPlaceTransformation()); + assertTrue(new BytesTransformer.ShiftTransformer(0, BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT, ByteOrder.BIG_ENDIAN).supportInPlaceTransformation()); assertTrue(new BytesTransformer.ReverseTransformer().supportInPlaceTransformation()); assertFalse(new BytesTransformer.MessageDigestTransformer("SHA1").supportInPlaceTransformation()); diff --git a/src/test/java/at/favre/lib/bytes/UtilByteTest.java b/src/test/java/at/favre/lib/bytes/UtilByteTest.java index 26180ea..95b55fb 100644 --- a/src/test/java/at/favre/lib/bytes/UtilByteTest.java +++ b/src/test/java/at/favre/lib/bytes/UtilByteTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import java.math.BigInteger; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.Random; @@ -162,21 +163,34 @@ private static void testReverse(byte[] input, int fromIndex, int toIndex, byte[] @Test public void testLeftShift() { byte[] test = new byte[]{0, 0, 1, 0}; - assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, -128, 0}, 1)); - assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, 64, 0}, 2)); - assertArrayEquals(new byte[]{1, 1, 1, 0}, Util.Byte.shiftLeft(new byte[]{-128, -128, -128, -128}, 1)); - assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 1)); - assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 2)); - assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 3)); - assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 8)); - assertArrayEquals(new byte[]{0, 2, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 9)); - assertArrayEquals(new byte[]{1, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 16)); - assertArrayEquals(new byte[]{2, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 17)); - assertArrayEquals(new byte[]{-128, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 23)); - assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24)); - assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24)); - - assertSame(test, Util.Byte.shiftLeft(test, 1)); + assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, -128, 0}, 1, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, 64, 0}, 2, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{1, 1, 1, 0}, Util.Byte.shiftLeft(new byte[]{-128, -128, -128, -128}, 1, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 1, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 2, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 3, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 8, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 2, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 9, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{1, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 16, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{2, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 17, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{-128, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 23, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24, ByteOrder.BIG_ENDIAN)); + + assertSame(test, Util.Byte.shiftLeft(test, 1, ByteOrder.BIG_ENDIAN)); + + assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.Byte.shiftLeft(new byte[]{0, 1, 0, 0}, 8, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0, 0, 1, 0}, Util.Byte.shiftLeft(new byte[]{(byte) 0, 1, 0, 1}, 8, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0x54, 0x1}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 1, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0xA8, 0x2}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 2, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0x50, 0x5}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 3, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0xA0, 0xA}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 4, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0x40, 0x15}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 5, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0x80, 0x2A}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 6, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0x00, 0x55}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 7, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0, (byte) 0xAA}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 8, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 0, 0x40}, Util.Byte.shiftLeft(new byte[]{1, 0, 0, 0}, 30, ByteOrder.LITTLE_ENDIAN)); + } @Test @@ -187,7 +201,7 @@ public void testLeftShiftAgainstRefImpl() { Bytes rnd = Bytes.random(4 + new Random().nextInt(14)); byte[] expected = Bytes.from(new BigInteger(rnd.array()).shiftLeft(shift).toByteArray()).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array(); - byte[] actual = Bytes.from(Util.Byte.shiftLeft(rnd.copy().array(), shift)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array(); + byte[] actual = Bytes.from(Util.Byte.shiftLeft(rnd.copy().array(), shift, ByteOrder.BIG_ENDIAN)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array(); System.out.println("Original \t" + rnd.encodeBinary() + " << " + shift); System.out.println("Expected \t" + Bytes.wrap(expected).encodeBinary()); @@ -202,18 +216,23 @@ public void testLeftShiftAgainstRefImpl() { public void testRightShift() { byte[] test = new byte[]{0, 0, 16, 0}; assertEquals(0b01111110, 0b11111101 >>> 1); - assertArrayEquals(new byte[]{0b00101101, (byte) 0b01111110}, Util.Byte.shiftRight(new byte[]{0b01011010, (byte) 0b11111101}, 1)); - assertArrayEquals(new byte[]{0, -128, -128, -128}, Util.Byte.shiftRight(new byte[]{1, 1, 1, 1}, 1)); - assertArrayEquals(new byte[]{0, -128, 66, 0}, Util.Byte.shiftRight(new byte[]{2, 1, 8, 2}, 2)); + assertArrayEquals(new byte[]{0b00101101, (byte) 0b01111110}, Util.Byte.shiftRight(new byte[]{0b01011010, (byte) 0b11111101}, 1, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, -128, -128, -128}, Util.Byte.shiftRight(new byte[]{1, 1, 1, 1}, 1, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, -128, 66, 0}, Util.Byte.shiftRight(new byte[]{2, 1, 8, 2}, 2, ByteOrder.BIG_ENDIAN)); assertArrayEquals(new byte[]{0, -128, 66, 0}, new BigInteger(new byte[]{2, 1, 8, 2}).shiftRight(2).toByteArray()); - assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(Bytes.from(test).array(), 5)); - assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(new byte[]{0, 0, 1, 0}, 1)); - assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 1)); - assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 2)); - assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 3)); - assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 4)); + assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(Bytes.from(test).array(), 5, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(new byte[]{0, 0, 1, 0}, 1, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 1, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 2, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 3, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 4, ByteOrder.BIG_ENDIAN)); + + assertSame(test, Util.Byte.shiftRight(test, 1, ByteOrder.BIG_ENDIAN)); - assertSame(test, Util.Byte.shiftRight(test, 1)); + assertArrayEquals(new byte[]{0, 0}, Util.Byte.shiftRight(new byte[]{1, 0}, 1, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0x80, 0}, Util.Byte.shiftRight(new byte[]{0, 0x02}, 2, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 0x80, 0, 0, 0}, Util.Byte.shiftRight(new byte[]{0, 0, 0, 1}, 17, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new byte[]{(byte) 1, 0, 0, 0}, Util.Byte.shiftRight(new byte[]{0, 0, 0, (byte) 0x80}, 31, ByteOrder.LITTLE_ENDIAN)); } @Test @@ -223,7 +242,7 @@ public void testRightShiftAgainstRefImpl() { Bytes rnd = Bytes.random(4 + new Random().nextInt(12)); if (!rnd.bitAt(rnd.lengthBit() - 1)) { //only unsigned byte[] expected = Bytes.from(new BigInteger(rnd.array()).shiftRight(shift).toByteArray()).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array(); - byte[] actual = Bytes.from(Util.Byte.shiftRight(rnd.copy().array(), shift)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array(); + byte[] actual = Bytes.from(Util.Byte.shiftRight(rnd.copy().array(), shift, ByteOrder.BIG_ENDIAN)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array(); // System.out.println("Original \t" + rnd.encodeBinary() + " >> " + shift); // System.out.println("Expected \t" + Bytes.wrap(expected).encodeBinary()); From e8eb89e252cd68bc982ffe5d4efd221eab556443 Mon Sep 17 00:00:00 2001 From: pfavre Date: Sat, 27 Feb 2021 23:04:11 +0100 Subject: [PATCH 04/39] Update CHANGELOG --- CHANGELOG | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 10b2ffc..d211c99 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,10 @@ # Releases +## v1.5.0 + +* fix `leftShift()` and `rightShift()` to respect byte order (thx @gfpeltier) +* fix `bitAt()` to respect byte order (thx @gfpeltier) + ## v1.4.0 * add `from()` constructor from `float[]` From 8a74c6b87f987ac11daf59de54ce2ecb336c3427 Mon Sep 17 00:00:00 2001 From: pfavre Date: Sat, 27 Feb 2021 23:23:44 +0100 Subject: [PATCH 05/39] Bump version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0a27b42..f2de9ac 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ bytes - 1.4.0 + 1.5.0 bundle Bytes Utility Library From 000e37c553a4b788854c1f19032a220d688ef6b3 Mon Sep 17 00:00:00 2001 From: michielj-webiq <80327153+michielj-webiq@users.noreply.github.com> Date: Tue, 9 Mar 2021 12:43:12 +0100 Subject: [PATCH 06/39] use buffer instead of byteBuffer in README The method to turn Bytes into a ByteBuffer is called `buffer`, not `byteBuffer` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb4a105..b7ac77f 100644 --- a/README.md +++ b/README.md @@ -498,7 +498,7 @@ Conversion to [`InputStream`](https://docs.oracle.com/javase/7/docs/api/java/io/ ```java Bytes.wrap(array).inputStream(); -Bytes.wrap(array).byteBuffer(); +Bytes.wrap(array).buffer(); ``` If you just want a duplicated instance, sharing the same array: From f3a0c68459c4b84a36ef72810e38a9f2a6dd5d74 Mon Sep 17 00:00:00 2001 From: Herman Lyakhovich <53296879+hlyakhovich@users.noreply.github.com> Date: Thu, 11 Mar 2021 16:54:56 +0100 Subject: [PATCH 07/39] Add toShortArray --- src/main/java/at/favre/lib/bytes/Bytes.java | 19 +++++++++++ src/main/java/at/favre/lib/bytes/Util.java | 28 +++++++++++++++ .../bytes/BytesToConvertOtherTypesTest.java | 34 +++++++++++++++++++ .../at/favre/lib/bytes/UtilConverterTest.java | 16 +++++++++ 4 files changed, 97 insertions(+) diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index f1ab738..42541e6 100644 --- a/src/main/java/at/favre/lib/bytes/Bytes.java +++ b/src/main/java/at/favre/lib/bytes/Bytes.java @@ -2044,6 +2044,25 @@ public double[] toDoubleArray() { return Util.Converter.toDoubleArray(internalArray(), byteOrder); } + /** + * Converts the internal byte array to a short array, that is, every 2 bytes will be packed into a single short. + *

+ * E.g. 2 bytes will be packed to a length 1 short array: + *

+     *  [b1, b2] = [short1]
+     * 
+ *

+ * This conversion respects the internal byte order. Will only work if all bytes can be directly mapped to short, + * which means the byte array length must be dividable by 2 without rest. + * + * @return new short[] instance representing this byte array + * @throws IllegalArgumentException if internal byte length mod 2 != 0 + */ + public short[] toShortArray() { + Util.Validation.checkModLength(length(), 2, "creating a short array"); + return Util.Converter.toShortArray(internalArray(), byteOrder); + } + /** * Decodes the internal byte array to UTF-8 char array. * This implementation will not internally create a {@link String}. diff --git a/src/main/java/at/favre/lib/bytes/Util.java b/src/main/java/at/favre/lib/bytes/Util.java index ee494fc..da50ff0 100644 --- a/src/main/java/at/favre/lib/bytes/Util.java +++ b/src/main/java/at/favre/lib/bytes/Util.java @@ -860,6 +860,34 @@ static double[] toDoubleArray(byte[] bytes, ByteOrder byteOrder) { return array; } + + /** + * Converts the byte array to a short array. This will spread 2 bytes into a single short: + * + *

+         *     [b1, b2] = [short1]
+         * 
+ * + *

+ * Analysis + *

    + *
  • Time Complexity: O(n)
  • + *
  • Space Complexity: O(n)
  • + *
  • Alters Parameters: false
  • + *
+ *

+ * + * @param bytes to convert to short array, must be % 2 == 0 to work correctly + * @param byteOrder of the byte array + * @return short array + */ + static short[] toShortArray(byte[] bytes, ByteOrder byteOrder) { + ShortBuffer buffer = ByteBuffer.wrap(bytes).order(byteOrder).asShortBuffer(); + short[] array = new short[buffer.remaining()]; + buffer.get(array); + return array; + } + /** * Convert UUID to a newly generated 16 byte long array representation. Puts the 8 byte most significant bits and * 8 byte least significant bits into an byte array. diff --git a/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java b/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java index 4c1ff0f..efa1559 100644 --- a/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java +++ b/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java @@ -406,4 +406,38 @@ public void testToDoubleArrayLittleEndian() { 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toDoubleArray(), 0.01); } + + @Test + public void testToShortArrayEmpty() { + assertArrayEquals(new short[0], Bytes.empty().toShortArray()); + } + + + @Test(expected = IllegalArgumentException.class) + public void testToShortArrayNotMod2Was5Byte() { + Bytes.wrap(new byte[]{0, 0, 0, 0, 1}).toShortArray(); + } + + @Test + public void testToShortArray() { + assertArrayEquals(new short[]{1}, Bytes.wrap(new byte[]{0, 1}).toShortArray()); + assertArrayEquals(new short[]{257}, Bytes.wrap(new byte[]{1, 1}).toShortArray()); + assertArrayEquals(new short[]{32_767}, Bytes.wrap(new byte[]{127, -1}).toShortArray()); + + assertArrayEquals(new short[]{1, 1}, Bytes.wrap(new byte[]{0, 1, 0, 1}).toShortArray()); + assertArrayEquals(new short[]{257, 1}, Bytes.wrap(new byte[]{1, 1, 0, 1}).toShortArray()); + assertArrayEquals(new short[]{257, 32_767, 1}, Bytes.wrap(new byte[]{1, 1, 127, -1, 0, 1}).toShortArray()); + } + + @Test + public void testToShortArrayLittleEndian() { + assertArrayEquals(new short[]{1}, Bytes.wrap(new byte[]{1, 0}, ByteOrder.LITTLE_ENDIAN).toShortArray()); + assertArrayEquals(new short[]{257}, Bytes.wrap(new byte[]{1, 1}, ByteOrder.LITTLE_ENDIAN).toShortArray()); + assertArrayEquals(new short[]{32_767}, Bytes.wrap(new byte[]{-1, 127}, ByteOrder.LITTLE_ENDIAN).toShortArray()); + + assertArrayEquals(new short[]{1, 1}, Bytes.wrap(new byte[]{1, 0, 1, 0}, ByteOrder.LITTLE_ENDIAN).toShortArray()); + assertArrayEquals(new short[]{257, 1}, Bytes.wrap(new byte[]{1, 1, 1, 0}, ByteOrder.LITTLE_ENDIAN).toShortArray()); + assertArrayEquals(new short[]{257, 32_767, 1}, Bytes.wrap(new byte[]{1, 1, -1, 127, 1, 0}, ByteOrder.LITTLE_ENDIAN).toShortArray()); + } + } diff --git a/src/test/java/at/favre/lib/bytes/UtilConverterTest.java b/src/test/java/at/favre/lib/bytes/UtilConverterTest.java index 8aa717f..3ac6f7b 100644 --- a/src/test/java/at/favre/lib/bytes/UtilConverterTest.java +++ b/src/test/java/at/favre/lib/bytes/UtilConverterTest.java @@ -193,4 +193,20 @@ public void testToDoubleArray() { assertArrayEquals(new double[0], Util.Converter.toDoubleArray(new byte[0], ByteOrder.LITTLE_ENDIAN), 0.01); assertArrayEquals(new double[0], Util.Converter.toDoubleArray(new byte[0], ByteOrder.BIG_ENDIAN), 0.01); } + + @Test + public void testToShortArray() { + assertArrayEquals(new short[]{1}, Util.Converter.toShortArray(new byte[]{0, 1}, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new short[]{257}, Util.Converter.toShortArray(new byte[]{1, 1}, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new short[]{32_767}, Util.Converter.toShortArray(new byte[]{127, -1}, ByteOrder.BIG_ENDIAN)); + assertArrayEquals(new short[]{257, 32_767, 1}, Util.Converter.toShortArray(new byte[]{1, 1, 127, -1, 0, 1}, ByteOrder.BIG_ENDIAN)); + + assertArrayEquals(new short[]{1}, Util.Converter.toShortArray(new byte[]{1, 0}, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new short[]{257}, Util.Converter.toShortArray(new byte[]{1, 1}, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new short[]{32_767}, Util.Converter.toShortArray(new byte[]{-1, 127}, ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new short[]{257, 32_767, 1}, Util.Converter.toShortArray(new byte[]{1, 1, -1, 127, 1, 0}, ByteOrder.LITTLE_ENDIAN)); + + assertArrayEquals(new short[0], Util.Converter.toShortArray(new byte[0], ByteOrder.LITTLE_ENDIAN)); + assertArrayEquals(new short[0], Util.Converter.toShortArray(new byte[0], ByteOrder.BIG_ENDIAN)); + } } From c0f45adc2ca696c0317edab2cf3eed89adaf17bc Mon Sep 17 00:00:00 2001 From: Herman Lyakhovich <53296879+hlyakhovich@users.noreply.github.com> Date: Wed, 17 Mar 2021 19:29:22 +0100 Subject: [PATCH 08/39] Overload from() with short array --- src/main/java/at/favre/lib/bytes/Bytes.java | 12 +++++++- src/main/java/at/favre/lib/bytes/Util.java | 28 ++++++++++++++++++- .../lib/bytes/BytesConstructorTests.java | 13 +++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index 42541e6..3797292 100644 --- a/src/main/java/at/favre/lib/bytes/Bytes.java +++ b/src/main/java/at/favre/lib/bytes/Bytes.java @@ -296,6 +296,16 @@ public static Bytes from(short short2Byte) { return wrap(ByteBuffer.allocate(2).putShort(short2Byte).array()); } + /** + * Creates a new instance from given 2 byte short array. + * + * @param shortArray to create from + * @return new instance + */ + public static Bytes from(short... shortArray) { + return wrap(Util.Converter.toByteArray(Objects.requireNonNull(shortArray, "must provide at least a single short"))); + } + /** * Creates a new instance from given 4 byte integer. * @@ -1492,7 +1502,7 @@ public Bytes duplicate() { /** * Set the byte order or endianness of this instance. Default in Java is {@link ByteOrder#BIG_ENDIAN}. *

- * This option is important for all encoding and conversation methods. + * This option is important for all encoding and conversion methods. * * @param byteOrder new byteOrder * @return a new instance with the same underlying array and new order, or "this" if order is the same diff --git a/src/main/java/at/favre/lib/bytes/Util.java b/src/main/java/at/favre/lib/bytes/Util.java index da50ff0..710c4d3 100644 --- a/src/main/java/at/favre/lib/bytes/Util.java +++ b/src/main/java/at/favre/lib/bytes/Util.java @@ -567,6 +567,33 @@ static byte[] toPrimitiveArray(java.lang.Byte[] objectArray) { return primitivesArray; } + /** + * Creates a byte array from given short array. + * The resulting byte array will have length shortArray * 2. + * + *

+ * Analysis + *

    + *
  • Time Complexity: O(n)
  • + *
  • Space Complexity: O(n)
  • + *
  • Alters Parameters: false
  • + *
+ *

+ * + * @param shortArray to convert + * @return resulting byte array + */ + static byte[] toByteArray(short[] shortArray) { + byte[] primitivesArray = new byte[shortArray.length * 2]; + ByteBuffer buffer = ByteBuffer.allocate(2); + for (int i = 0; i < shortArray.length; i++) { + buffer.clear(); + byte[] shortBytes = buffer.putShort(shortArray[i]).array(); + System.arraycopy(shortBytes, 0, primitivesArray, (i * 2), shortBytes.length); + } + return primitivesArray; + } + /** * Creates a byte array from given int array. * The resulting byte array will have length intArray * 4. @@ -860,7 +887,6 @@ static double[] toDoubleArray(byte[] bytes, ByteOrder byteOrder) { return array; } - /** * Converts the byte array to a short array. This will spread 2 bytes into a single short: * diff --git a/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java b/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java index 612dba3..5cf5058 100644 --- a/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java +++ b/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java @@ -179,6 +179,19 @@ public void fromShort() { assertEquals(test, Bytes.from(test).toShort()); } + @Test(expected = NullPointerException.class) + public void fromShortArray_empty_shouldThrow() { + Bytes.from((short[]) null); + } + + @Test + public void fromShortArray() { + assertArrayEquals(new byte[]{0, 1, 0, 2}, Bytes.from((short) 1, (short) 2).array()); + assertArrayEquals(Bytes.from(Bytes.from((short) 32767), Bytes.from((short) 6761), Bytes.from((short) -8517)).array(), Bytes.from((short) 32767, (short) 6761, (short) -8517).array()); + assertArrayEquals(Bytes.from(Bytes.from((short) 1678), Bytes.from((short) -223), Bytes.from((short) 11114)).array(), Bytes.from((short) 1678, (short) -223, (short) 11114).array()); + assertArrayEquals(new byte[]{114, -123, 35, 53, 0, 0, 56, -70}, Bytes.from((short) 29317, (short) 9013, (short) 0, (short) 14522).array()); + } + @Test public void fromInt() { int test = 722837193; From c4b411acaebc747ed3e6ea1adc9e92404633223c Mon Sep 17 00:00:00 2001 From: Herman Lyakhovich <53296879+hlyakhovich@users.noreply.github.com> Date: Wed, 17 Mar 2021 19:42:29 +0100 Subject: [PATCH 09/39] Correct Checkstyle clause in docs --- CONTRIBUTING.md | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a0c2d5..7404d32 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,13 +2,13 @@ We ❤ pull requests from everyone. -If possible proof features and bug fixes with unit tests. +If possible to proof features and bug fixes with unit tests. This repo validates against checkstyle (import the xml found in the root to your IDE if possible) To run the tests (and checkstyle): ```shell -mvn test +mvn verify ``` Tests are automatically run against branches and pull requests diff --git a/README.md b/README.md index b7ac77f..96e0a0b 100644 --- a/README.md +++ b/README.md @@ -694,7 +694,7 @@ Use the Maven wrapper to create a jar including all dependencies ### Checkstyle Config File This project uses my [`common-parent`](https://github.com/patrickfav/mvn-common-parent) which centralized a lot of -the plugin versions aswell as providing the checkstyle config rules. Specifically they are maintained in [`checkstyle-config`](https://github.com/patrickfav/checkstyle-config). Locally the files will be copied after you `mvnw install` into your `target` folder and is called +the plugin versions as well as providing the checkstyle config rules. Specifically they are maintained in [`checkstyle-config`](https://github.com/patrickfav/checkstyle-config). Locally the files will be copied after you `mvnw install` into your `target` folder and is called `target/checkstyle-checker.xml`. So if you use a plugin for your IDE, use this file as your local configuration. ## Tech Stack From 8796a66e3ab1e163f7d660fddfd558f147013bc4 Mon Sep 17 00:00:00 2001 From: airsquared <36649395+airsquared@users.noreply.github.com> Date: Mon, 29 Mar 2021 22:38:40 -0700 Subject: [PATCH 10/39] Add an automatic module name to support the JPMS --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index f2de9ac..fe145e2 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,16 @@ net.nicoulaj.maven.plugins checksum-maven-plugin + + maven-jar-plugin + + + + at.favre.lib.bytes + + + + From 966feae09b0b5d0b02467e6824c5d037ca18ea39 Mon Sep 17 00:00:00 2001 From: Herman Lyakhovich <53296879+hlyakhovich@users.noreply.github.com> Date: Sat, 3 Sep 2022 15:21:34 +0200 Subject: [PATCH 11/39] Add toIndex versions for indexOf() --- src/main/java/at/favre/lib/bytes/Bytes.java | 33 +++++++++++++++++++ .../at/favre/lib/bytes/BytesMiscTest.java | 19 +++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index 3797292..c2b5b20 100644 --- a/src/main/java/at/favre/lib/bytes/Bytes.java +++ b/src/main/java/at/favre/lib/bytes/Bytes.java @@ -1277,6 +1277,20 @@ public int indexOf(byte target, int fromIndex) { return indexOf(new byte[]{target}, fromIndex); } + /** + * Returns the index of the first appearance of the value {@code target} in + * {@code array} from given start index 'fromIndex' to given end index 'toIndex'. + * + * @param target a primitive {@code byte} value + * @param fromIndex search from this index + * @param toIndex search to this index + * @return the least index {@code i} for which {@code array[i] == target}, or + * {@code -1} if no such index exists or fromIndex is gt target length. + */ + public int indexOf(byte target, int fromIndex, int toIndex) { + return indexOf(new byte[]{target}, fromIndex, toIndex); + } + /** * Returns the start position of the first occurrence of the specified {@code * target} within {@code array}, or {@code -1} if there is no such occurrence. @@ -1311,6 +1325,25 @@ public int indexOf(byte[] subArray, int fromIndex) { return Util.Byte.indexOf(internalArray(), subArray, fromIndex, length()); } + /** + * Returns the start position of the first occurrence of the specified {@code + * target} within {@code array} from given start index 'fromIndex' to given end + * index 'toIndex', or {@code -1} if there is no such occurrence. + *

+ * More formally, returns the lowest index {@code i} such that {@code + * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly + * the same elements as {@code target}. + * + * @param subArray the array to search for as a sub-sequence of {@code array} + * @param fromIndex search from this index + * @param toIndex search to this index + * @return the least index {@code i} for which {@code array[i] == target}, or + * {@code -1} if no such index exists. + */ + public int indexOf(byte[] subArray, int fromIndex, int toIndex) { + return Util.Byte.indexOf(internalArray(), subArray, fromIndex, toIndex); + } + /** * Checks if the given sub array is equal to the start of given array. That is, sub array must be gt or eq * to the length of the internal array and internal[i] == subArray[i] for i=0..subArray.length-1 diff --git a/src/test/java/at/favre/lib/bytes/BytesMiscTest.java b/src/test/java/at/favre/lib/bytes/BytesMiscTest.java index ed832bb..a57398d 100644 --- a/src/test/java/at/favre/lib/bytes/BytesMiscTest.java +++ b/src/test/java/at/favre/lib/bytes/BytesMiscTest.java @@ -188,6 +188,15 @@ public void indexOfByteFromIndex() { assertEquals(10, Bytes.from(example_bytes_sixteen).indexOf((byte) 0xFD, 5)); } + @Test + public void indexOfByteFromIndexToIndex() { + assertEquals(4, Bytes.from(example_bytes_seven).indexOf((byte) 0x1E, 0, 7)); + assertEquals(4, Bytes.from(example_bytes_seven).indexOf((byte) 0x1E, 3, 5)); + assertEquals(-1, Bytes.from(example_bytes_seven).indexOf((byte) 0x1E, 0, 3)); + assertEquals(-1, Bytes.from(example_bytes_seven).indexOf((byte) 0x1E, 6, 7)); + assertEquals(-1, Bytes.from(example_bytes_seven).indexOf((byte) 0xCA, 0, 7)); + } + @Test public void indexOfArray() { assertEquals(-1, Bytes.allocate(0).indexOf(new byte[]{(byte) 0xFD})); @@ -204,6 +213,16 @@ public void indexOfArrayFromIndex() { assertEquals(2, Bytes.from(new byte[]{(byte) 0x8E, (byte) 0xD1, (byte) 0x8E, (byte) 0xD1, 0x12, (byte) 0xAF, (byte) 0x78, 0x09, 0x1E, (byte) 0xD1, (byte) 0xFD, (byte) 0xAA, 0x12}).indexOf(new byte[]{(byte) 0x8E, (byte) 0xD1}, 1)); } + @Test + public void indexOfArrayFromIndexToIndex() { + assertEquals(4, Bytes.from(example_bytes_seven).indexOf(new byte[] { (byte) 0x1E, (byte) 0xAF }, 0, 7)); + assertEquals(4, Bytes.from(example_bytes_seven).indexOf(new byte[] { (byte) 0x1E, (byte) 0xAF }, 3, 5)); + assertEquals(4, Bytes.from(example_bytes_seven).indexOf(new byte[] { (byte) 0x1E, (byte) 0xAF, (byte) 0xED }, 4, 5)); + assertEquals(-1, Bytes.from(example_bytes_seven).indexOf(new byte[] { (byte) 0x1E, (byte) 0xAF }, 0, 3)); + assertEquals(-1, Bytes.from(example_bytes_seven).indexOf(new byte[] { (byte) 0x1E, (byte) 0xAF }, 6, 7)); + assertEquals(-1, Bytes.from(example_bytes_seven).indexOf(new byte[] { (byte) 0xCA, (byte) 0xFE }, 0, 7)); + } + @Test public void startsWidth() { assertFalse(Bytes.allocate(0).startsWith(new byte[1])); From 0b6dd028c9fa90a0623580a2766bde0585f060a0 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sat, 11 Feb 2023 18:46:31 +0100 Subject: [PATCH 12/39] Migrate to Github Actions, CodeCov and Maven Central and update maven wrapper and deps --- .github/workflows/build_deploy.yml | 64 +++++++++++++ .mvn/wrapper/MavenWrapperDownloader.java | 117 ----------------------- .mvn/wrapper/maven-wrapper.jar | Bin 50710 -> 59925 bytes .mvn/wrapper/maven-wrapper.properties | 20 +++- .travis.yml | 51 ---------- README.md | 4 +- mvnw | 99 ++++++++----------- mvnw.cmd | 39 ++++---- pom.xml | 32 ++++--- secrets.tar.enc | Bin 6672 -> 0 bytes 10 files changed, 163 insertions(+), 263 deletions(-) create mode 100644 .github/workflows/build_deploy.yml delete mode 100644 .mvn/wrapper/MavenWrapperDownloader.java delete mode 100644 .travis.yml delete mode 100644 secrets.tar.enc diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml new file mode 100644 index 0000000..3fd75e4 --- /dev/null +++ b/.github/workflows/build_deploy.yml @@ -0,0 +1,64 @@ +name: Build and Deploy with Maven + +on: + push: + branches: + - main + tags: + - '*' # Trigger on all tags + pull_request: { } + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + cache: 'maven' + - name: Build with Maven + run: ./mvnw -B clean package checkstyle:checkstyle jacoco:report -DcommonConfig.jarSign.skip=true + - name: Upload coverage reports to CodeCov + uses: codecov/codecov-action@v3 + + deploy: + needs: build + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Retrieve Keystore from secrets + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + run: | + echo $KEYSTORE_BASE64 | base64 --decode > keystore.jks + - name: Set up Maven Central Repository + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + cache: 'maven' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Publish package + run: mvn -B deploy -DskipTests + env: + OPENSOURCE_PROJECTS_KS_PW: ${{ secrets.KEYSTORE_PASSWORD }} + OPENSOURCE_PROJECTS_KEY_PW: ${{ secrets.KEYSTORE_KEY_PASSWORD }} + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + - name: Create and upload Github Release + uses: xresloader/upload-to-github-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: "target/*.jar;target/*.sha256;target/checksum-sha256.txt" + tags: true + draft: false diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index b901097..0000000 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index 2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054..bf82ff01c6cdae4a1bb754a6e062954d77ac5c11 100644 GIT binary patch literal 59925 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

dy!05UMhSKsK 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 VCNKd3JGtOvEm(5t0lFyE{{i--k}m)N literal 50710 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 EfAhh=DF6Tf diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 642d572..ca5ab4b 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,18 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f6b9abc..0000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -# To let the CI execute the maven wrapper, use this command and push the change: -# git update-index --chmod=+x mvnw - -language: java -jdk: - - openjdk8 - -env: - global: - - secure: "eV7hLuGYyG1daQIdZ4p2EtSu0vwZSC9aVpEz1/6JWypdAJKu6uLxibHtLXHqSB8PWXDJbpNIhs7gaD8NGBz506U0V+XG1eMYhJ4qonw4Xayk5HATrGW2aWxT1t8W/ZVpb+Z9r3D6N7VvxvbzzV1yUJYc0RJCHT2MklCdjv2CVdZMErDdg3tCQihP7AWm3Z7Ab4+33STcOzNkZNyiOKu9UQBSvu2EoBBrzEbjJo+tZyn4iEC4DVLxA8tLeHi7XTD6DhzgTdyUTM1YAvwNxNrkMewG4tPV8sH+sdj80Qqt4h2Wg+LfoYUwT59HFPcoMdmPauQbaSkqMMjVpwvtWXoJgWCNYRg3Us/7vtml4YTqdj2ao38QvL/QATyOXy/0kilhp32pfyY5hbknxseLr/dShhsrofbThUfxyK8As6FfeIwOywU/TNRl2D7WfQwtAL9nv5RRkNj+EVZjE7J4pl2gL4wn6O18m04stGgkoXSjKa2Zeiy4dd5Nb7FyYSL5CWhMevPVOwUNKZkz3Ys3v/DEmelerCIfFofFlULtyFc4MVWDZpHA2EJd78DC/P3MuiL8aElICRenIpvgHShxFnrGbvomWW60EKuexkM5Hrcn8oyNx7zDPwrqw0hgM8xEZCJymVu01G/JlthjynzFvR7WBxRGwAPxTxBh5adzWZdr6gw=" - - secure: "BqaCKfW/yyBh0jzrMloCxvDECKtg8qa/AxJNlw1IInQPobN7MLH/hGaGtuArG0zfY8pu0w5nGYGkmo/RgW4kgdd/YQ1UIZNS8gBoZh5S7dtSg9EXV+oXlhOfIzictJtL1sJjCGVhzlIRWj5HTysdrAY4Lg/QrjnyX7R5vSdNPnCdDyg7dIsg6r1ROSt8bjQCWxJpNLA3BrG1hvBqP2QLNmlCz7OlFmNPok9PfMumSa8VT8WpqwghrBOCEFf32wRHAkpXjQgQ5biXL2iPgTbEvfoU1ePn9N/OaUYl5mVKcDc1YnTQeMHg7tvciSZ1DZfuiCrTsnnUq8DXVghsjt3zDASlFyTvgf1PSCTjOezNEP/LZw9BXeMo5+bETZLMCcSaSZTUjnNgMRodKPXT3T8v22+5vjvC1NjZCeYdNCt0bxu+/ETdPxh4Lo7L5SNJ/rAurAKXXWt2ARmBrm5nGIiZNyOGjec59Y4ekfDqut87YNmbeqZgxxIoz40EwoRsfT0On7xss8SQi/uKhBwt9i/RW1ihyFAQLymKuSE3ChUkCVwzy/SmTduRUDhoSy982qFMnTPmjMDizfXXP2DjtPqZOExd2/AZsPmAz0N5FjCWciTHWKmhLr358SzgJIpXL+cYkJIR8pk038NKLvzlV0LQb1nrA20iiggjFzyRbwC60oE=" - -before_install: - - openssl aes-256-cbc -K $encrypted_b9f5be6a7c0d_key -iv $encrypted_b9f5be6a7c0d_iv -in secrets.tar.enc -out secrets.tar -d - - tar xvf secrets.tar - -script: - - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then ./mvnw clean install -Djarsigner.skip=true; fi' - - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./mvnw clean install -Djarsigner.skip=false; fi' - -after_success: - - ./mvnw test jacoco:report coveralls:report - -deploy: - - provider: script - script: ./mvnw -s ci-settings.xml deploy -X - skip_cleanup: true - on: - branch: master - tags: true - - provider: releases - api_key: - secure: "ozRga/xe4pNZNH59tmPK1lLpCFa4WFW674JMTBWLbf+7zjPiN8K5dtAYUkhDqngw0ifasukVCRey4F9fu479KlFciGrLBd4r/hq/rh7inVHqd1qcn4AwyDX3EkbloD5qwfas7yn5zJ7cWm7KyEkfpr+fgReaQzSRvJUpjpDBrS2xNIYOhL1tInu84/RtyB6QuJjQOkH9rulP9M8gjcEH9Iu0k40jAVYLJDL9opOWrc9qpAzL0wN4xXGbnI30WrA8nmLcrHRxODOxbYEQ5EndLFJyS3nYxL9D3QJeEYkXf9uqz46V4dgfrEirWrtin3PlSUKTD9/0Mqz4uxqrGky0vrpha7m231+ilTUJzvE0a29LdS3R4A6ssDb0oFtj4pP3O6uNDqZN20r83ughGi0GrlKL/CGpQ4fS5nOpqppx0Xg+SR78/uOqQDIOn570FILBTKiaTsCn6xKqYC4QFSlsZiXojgU4FDZpKygH932qUDE0BnzsZEtzCube2oXMktZs6keSmELMyRYVs/K9yC7+8R1Pez1Xz72zxNG1ke6588sO3wxN+Q+0ZhqmQC8Y2OY8i72b06M5SyGz5TV38SrntLU88HOMWVJtfRDcMcPnJdFgaujZCoOw5SfokzXncipldfJGpKGIb3fHBMJw0MzspT7Kp6XRzSbtJI5+NVestR4=" - file_glob: true - file: - - "target/*.jar" - - "target/*.sha256" - - "target/checksum-sha256.txt" - skip_cleanup: true - on: - branch: master - tags: true - -cache: - directories: - - $HOME/.m2 - -notifications: - slack: - secure: "PiEdsZoQlBR7ce6IT9LlxWod2XqWjm8HCxs4yP8o9iI5ix7FxEV8NDGQKY1vcDG6D8bz4gcNsBxJrPKASD0G3rLaJpjA4L9MUNhyx+4VCIBBnT1fwV+as+PJmr1tZupqUnlxZJyTdiaqkV8jtjCkHUn3P6TURbNuZws74/ki2rICbi4EebD3mdeQvFtWYKTOsAgZjLecMvPU9q+b7TAwDlaZFbkG7DMFruT8i7tA/EWMOvI48kVaUEM0QPsAxNdr32YFta0s/xJyNuOp1Hp3ZZbnZgXgKc2WvGRcg+9sucnQ+eQsQKj1tbWS2YwxHcy7B3Q7YDmBL2NIJIHbuaWNlbK9fVh3gdk7Knlr8IHpLmVuHvH3HAF6JDcrARjk8ViVP2HafkkDNbQZLupz5LkJ02hj1DMGVOAyuVTt6IpOMt3WTaQ66qtyGupQXhrqvq8tDwvg6FIy1sjPZbWE/Sq1s20PYBbHkVwNvQ3bNaC44nAkcnTv+dAoazvy8G40RIPovTO1ubzriZvuY8Db4THF2ZL1mDbyHEJstA7L9qkObqZOwnFsy5pLp4NfR9C/bTAY8EUcZVGm0spIADafip2H57Hf+n3co4aqw689t+Y453AUh3W+k4ZsC/V1kzX3MVdcluRP7ET8ejtfwduMyh2/jFmUK3g246/teEssyohp6ik=" - email: false diff --git a/README.md b/README.md index 96e0a0b..c39f125 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ to blindly paste code snippets from [o](https://stackoverflow.com/a/9670279/774398) [m](https://stackoverflow.com/questions/1519736/random-shuffling-of-an-array) -[![Download](https://api.bintray.com/packages/patrickfav/maven/bytes-java/images/download.svg)](https://bintray.com/patrickfav/maven/bytes-java/_latestVersion) +[![Maven Central](https://img.shields.io/maven-central/v/at.favre.lib/bytes)](https://mvnrepository.com/artifact/at.favre.lib/bytes) [![Build Status](https://travis-ci.com/patrickfav/bytes-java.svg?branch=master)](https://travis-ci.com/patrickfav/bytes-java) [![Javadocs](https://www.javadoc.io/badge/at.favre.lib/bytes.svg)](https://www.javadoc.io/doc/at.favre.lib/bytes) -[![Coverage Status](https://coveralls.io/repos/github/patrickfav/bytes-java/badge.svg?branch=master)](https://coveralls.io/github/patrickfav/bytes-java?branch=master) +[![codecov](https://codecov.io/gh/patrickfav/bytes-java/branch/master/graph/badge.svg?token=YiSRwBApvz)](https://codecov.io/gh/patrickfav/bytes-java) [![Maintainability](https://api.codeclimate.com/v1/badges/43b7770f0ee00b85f92a/maintainability)](https://codeclimate.com/github/patrickfav/bytes-java/maintainability) It's main features include: diff --git a/mvnw b/mvnw index 41c0f0c..b7f0646 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script +# Apache Maven Wrapper startup batch script, version 3.1.1 # # Required ENV vars: # ------------------ @@ -27,7 +27,6 @@ # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -36,6 +35,10 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi @@ -58,9 +61,9 @@ case "`uname`" in # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" + JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME else - export JAVA_HOME="/Library/Java/Home" + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi fi ;; @@ -72,36 +75,8 @@ if [ -z "$JAVA_HOME" ] ; then fi fi -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && @@ -110,8 +85,6 @@ fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" fi @@ -145,7 +118,7 @@ if [ -z "$JAVACMD" ] ; then JAVACMD="$JAVA_HOME/bin/java" fi else - JAVACMD="`which java`" + JAVACMD="`\\unset -f command; \\command -v java`" fi fi @@ -159,12 +132,9 @@ if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { - if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" @@ -184,7 +154,7 @@ find_maven_basedir() { fi # end of workaround done - echo "${basedir}" + printf '%s' "$(cd "$basedir"; pwd)" } # concatenates all lines of a file @@ -194,11 +164,16 @@ concat_lines() { fi } -BASE_DIR=`find_maven_basedir "$(pwd)"` +BASE_DIR=$(find_maven_basedir "$(dirname $0)") if [ -z "$BASE_DIR" ]; then exit 1; fi +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi + ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. @@ -212,16 +187,16 @@ else echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" fi while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; esac done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" + echo "Downloading from: $wrapperUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" if $cygwin; then @@ -229,42 +204,49 @@ else fi if command -v wget > /dev/null; then + QUIET="--quiet" if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" + QUIET="" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" elif command -v curl > /dev/null; then + QUIET="--silent" if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" + QUIET="" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L fi - + [ $? -eq 0 ] || rm -f "$wrapperJarPath" else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then + javaSource=`cygpath --path --windows "$javaSource"` javaClass=`cygpath --path --windows "$javaClass"` fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo " - Compiling MavenWrapperDownloader.java ..." fi # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") + ("$JAVA_HOME/bin/javac" "$javaSource") fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ -e "$javaClass" ]; then # Running the downloader if [ "$MVNW_VERBOSE" = true ]; then echo " - Running MavenWrapperDownloader.java ..." @@ -278,16 +260,10 @@ fi # End of extension ########################################################################################## -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && @@ -305,6 +281,7 @@ WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 8611571..474c9d6 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,13 +18,12 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script +@REM Apache Maven Wrapper startup batch script, version 3.1.1 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @@ -46,8 +45,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal @@ -120,10 +119,10 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @@ -134,11 +133,11 @@ if exist %WRAPPER_JAR% ( ) ) else ( if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% + echo Downloading from: %WRAPPER_URL% ) powershell -Command "&{"^ @@ -146,7 +145,7 @@ if exist %WRAPPER_JAR% ( "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ "}" if "%MVNW_VERBOSE%" == "true" ( echo Finished downloading %WRAPPER_JAR% @@ -158,7 +157,13 @@ if exist %WRAPPER_JAR% ( @REM work with both Windows and non-Windows executions. set MAVEN_CMD_LINE_ARGS=%* -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end @@ -168,15 +173,15 @@ set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause +if "%MAVEN_BATCH_PAUSE%"=="on" pause -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% -exit /B %ERROR_CODE% +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index fe145e2..16b6654 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ at.favre.lib common-parent - 12 + 15 bytes @@ -28,15 +28,25 @@ - jcenter - jcenter - https://jcenter.bintray.com + central + Maven Central + https://repo1.maven.org/maven2 false + + + central + Maven Central + https://repo1.maven.org/maven2 + + false + + + @@ -76,10 +86,6 @@ org.jacoco jacoco-maven-plugin - - org.eluder.coveralls - coveralls-maven-plugin - com.github.wvengen proguard-maven-plugin @@ -115,22 +121,22 @@ org.openjdk.jmh jmh-core - 1.21 + 1.35 test org.openjdk.jmh jmh-generator-annprocess - 1.21 + 1.35 test - bintray-patrickfav - patrickfav-bytes-java - https://api.bintray.com/maven/patrickfav/maven/bytes-java/;publish=1 + ossrh + Central Repository OSSRH + https://oss.sonatype.org/service/local/staging/deploy/maven2/ diff --git a/secrets.tar.enc b/secrets.tar.enc deleted file mode 100644 index 880e62b06a43123ed16df02f22a8cba201a283e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6672 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 Date: Sat, 11 Feb 2023 18:56:52 +0100 Subject: [PATCH 13/39] Bump to version 1.6.0 and update changelog --- CHANGELOG | 9 +++++++++ pom.xml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index d211c99..7cdb7ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,14 @@ # Releases +## v1.6.0 + +* migrate to github actions, codecov and maven central #49 +* add `indexOf` (thx @hlyakhovich) #48 +* add `toShortArray` (thx @hlyakhovich) #44 +* add `from()` constructor from `short` vararg or array (thx @hlyakhovich) #45 +* add an automatic module name to support the JPMS (thx @airsquared) #47 +* fix warning of junit 4.13 CVE-2020-15250 + ## v1.5.0 * fix `leftShift()` and `rightShift()` to respect byte order (thx @gfpeltier) diff --git a/pom.xml b/pom.xml index 16b6654..f0c3ca8 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ bytes - 1.5.0 + 1.6.0 bundle Bytes Utility Library From e07967f46ee9351d77514020d326487720e326d3 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sat, 11 Feb 2023 18:57:38 +0100 Subject: [PATCH 14/39] Update junit to fix warning of CVE-2020-15250 --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index f0c3ca8..d553e4f 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,7 @@ junit junit + 4.13.2 test From 35a7b7abcadf207310e02b34103a30587f46af22 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sat, 11 Feb 2023 19:40:59 +0100 Subject: [PATCH 15/39] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c39f125..8171298 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ to blindly paste code snippets from [m](https://stackoverflow.com/questions/1519736/random-shuffling-of-an-array) [![Maven Central](https://img.shields.io/maven-central/v/at.favre.lib/bytes)](https://mvnrepository.com/artifact/at.favre.lib/bytes) -[![Build Status](https://travis-ci.com/patrickfav/bytes-java.svg?branch=master)](https://travis-ci.com/patrickfav/bytes-java) +[![Github Actions](https://github.com/patrickfav/bytes-java/actions/workflows/build_deploy.yml/badge.svg)](https://github.com/patrickfav/bytes-java/actions) [![Javadocs](https://www.javadoc.io/badge/at.favre.lib/bytes.svg)](https://www.javadoc.io/doc/at.favre.lib/bytes) [![codecov](https://codecov.io/gh/patrickfav/bytes-java/branch/master/graph/badge.svg?token=YiSRwBApvz)](https://codecov.io/gh/patrickfav/bytes-java) [![Maintainability](https://api.codeclimate.com/v1/badges/43b7770f0ee00b85f92a/maintainability)](https://codeclimate.com/github/patrickfav/bytes-java/maintainability) From 8b9eae464c3846445ef8e6b1d619fac1bb21a1de Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sat, 11 Feb 2023 22:57:44 +0100 Subject: [PATCH 16/39] Fix missing gpg sign and add correct maven deploy flow --- .github/workflows/build_deploy.yml | 12 ++++++--- pom.xml | 41 ++++-------------------------- 2 files changed, 13 insertions(+), 40 deletions(-) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index 3fd75e4..5794ee6 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -44,16 +44,20 @@ jobs: java-version: '8' distribution: 'adopt' cache: 'maven' - server-id: ossrh - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD + server-username: MAVEN_USERNAME # env variable for username in deploy + server-password: MAVEN_PASSWORD # env variable for token in deploy + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import + gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Publish package - run: mvn -B deploy -DskipTests + run: | + ./mvnw -B verify nexus-staging:deploy -P deploy && \ + ./mvnw -B nexus-staging:release -P deploy env: OPENSOURCE_PROJECTS_KS_PW: ${{ secrets.KEYSTORE_PASSWORD }} OPENSOURCE_PROJECTS_KEY_PW: ${{ secrets.KEYSTORE_KEY_PASSWORD }} MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - name: Create and upload Github Release uses: xresloader/upload-to-github-release@v1 env: diff --git a/pom.xml b/pom.xml index d553e4f..1c665e9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ at.favre.lib common-parent - 15 + 18 bytes @@ -26,27 +26,6 @@ false - - - central - Maven Central - https://repo1.maven.org/maven2 - - false - - - - - - - central - Maven Central - https://repo1.maven.org/maven2 - - false - - - @@ -116,7 +95,6 @@ junit junit - 4.13.2 test @@ -133,18 +111,9 @@ - - - ossrh - Central Repository OSSRH - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - https://github.com/patrickfav/bytes-java.git - https://github.com/patrickfav/bytes-java.git - HEAD + scm:git:https://github.com/patrickfav/bytes-java.git + scm:git:https://github.com/patrickfav/bytes-java.git https://github.com/patrickfav/bytes-java @@ -154,7 +123,7 @@ - Travis - https://travis-ci.com/patrickfav/bytes-java + Github Actions + https://github.com/patrickfav/bytes-java/actions From 63eecac24ded096b4a5cf7855aae2757f7132b45 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sat, 11 Feb 2023 23:08:50 +0100 Subject: [PATCH 17/39] Fix deployment config --- .github/workflows/build_deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index 5794ee6..efbcc8a 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -44,6 +44,7 @@ jobs: java-version: '8' distribution: 'adopt' cache: 'maven' + server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml server-username: MAVEN_USERNAME # env variable for username in deploy server-password: MAVEN_PASSWORD # env variable for token in deploy gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import From ff440e68c941b039d809d3169dac7ae73f637abc Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Wed, 15 Feb 2023 19:03:28 +0100 Subject: [PATCH 18/39] Add sonar analyzing and remove codecov --- .github/workflows/build_deploy.yml | 36 +++++++++++++++++++++++++----- README.md | 7 +++--- pom.xml | 6 +++++ 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index efbcc8a..3f7e9d2 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -8,6 +8,9 @@ on: - '*' # Trigger on all tags pull_request: { } +env: + SONARQUBE_PROJECT: patrickfav_bytes-java + jobs: build: runs-on: ubuntu-latest @@ -15,16 +18,33 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v3 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven + id: cache-primes + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 - name: Set up JDK 8 uses: actions/setup-java@v3 with: java-version: '8' distribution: 'adopt' - cache: 'maven' - name: Build with Maven - run: ./mvnw -B clean package checkstyle:checkstyle jacoco:report -DcommonConfig.jarSign.skip=true - - name: Upload coverage reports to CodeCov - uses: codecov/codecov-action@v3 + run: ./mvnw -B clean verify checkstyle:checkstyle jacoco:report -DcommonConfig.jarSign.skip=true + - name: Analyze with SonaQube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=$SONARQUBE_PROJECT deploy: needs: build @@ -38,12 +58,18 @@ jobs: KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} run: | echo $KEYSTORE_BASE64 | base64 --decode > keystore.jks + - name: Cache Maven + id: cache-primes + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 - name: Set up Maven Central Repository uses: actions/setup-java@v3 with: java-version: '8' distribution: 'adopt' - cache: 'maven' server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml server-username: MAVEN_USERNAME # env variable for username in deploy server-password: MAVEN_PASSWORD # env variable for token in deploy diff --git a/README.md b/README.md index 8171298..c9096b4 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,11 @@ to blindly paste code snippets from [![Maven Central](https://img.shields.io/maven-central/v/at.favre.lib/bytes)](https://mvnrepository.com/artifact/at.favre.lib/bytes) [![Github Actions](https://github.com/patrickfav/bytes-java/actions/workflows/build_deploy.yml/badge.svg)](https://github.com/patrickfav/bytes-java/actions) [![Javadocs](https://www.javadoc.io/badge/at.favre.lib/bytes.svg)](https://www.javadoc.io/doc/at.favre.lib/bytes) -[![codecov](https://codecov.io/gh/patrickfav/bytes-java/branch/master/graph/badge.svg?token=YiSRwBApvz)](https://codecov.io/gh/patrickfav/bytes-java) -[![Maintainability](https://api.codeclimate.com/v1/badges/43b7770f0ee00b85f92a/maintainability)](https://codeclimate.com/github/patrickfav/bytes-java/maintainability) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bytes-java&metric=coverage)](https://sonarcloud.io/summary/new_code?id=patrickfav_bytes-java) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bytes-java&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=patrickfav_bytes-java) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bytes-java&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=patrickfav_bytes-java) -It's main features include: +Its main features include: * **Creation** from a wide variety of sources: multiple arrays, integers, [streams](https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html), random, strings, files, uuid, ... * **Transformation** with many built-in: append, [xor](https://en.wikipedia.org/wiki/Exclusive_or), [and](https://en.wikipedia.org/wiki/Logical_conjunction), [hash](https://en.wikipedia.org/wiki/Cryptographic_hash_function), [shifts](https://en.wikipedia.org/wiki/Bitwise_operation#Bit_shifts), shuffle, reverse, [checksum](https://en.wikipedia.org/wiki/Checksum), ... diff --git a/pom.xml b/pom.xml index 1c665e9..045e102 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,12 @@ false + + patrickfav + https://sonarcloud.io + jacoco + reuseReports + java From 528413b2575d06364fb7cae01bfd81c6ca875a87 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Wed, 15 Feb 2023 19:14:28 +0100 Subject: [PATCH 19/39] Remove errorprone and proguard optimized version to be able to build with JDK11 Not yet JDK17 compatible --- .github/workflows/build_deploy.yml | 10 +++++----- .mvn/maven.config | 2 +- README.md | 4 ++-- pom.xml | 8 ++------ 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index 3f7e9d2..607a0ca 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -33,11 +33,11 @@ jobs: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v3 with: - java-version: '8' - distribution: 'adopt' + java-version: '11' + distribution: 'temurin' - name: Build with Maven run: ./mvnw -B clean verify checkstyle:checkstyle jacoco:report -DcommonConfig.jarSign.skip=true - name: Analyze with SonaQube @@ -68,8 +68,8 @@ jobs: - name: Set up Maven Central Repository uses: actions/setup-java@v3 with: - java-version: '8' - distribution: 'adopt' + java-version: '11' + distribution: 'temurin' server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml server-username: MAVEN_USERNAME # env variable for username in deploy server-password: MAVEN_PASSWORD # env variable for token in deploy diff --git a/.mvn/maven.config b/.mvn/maven.config index b9d8785..f1099e4 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1 +1 @@ --DcommonConfig.compiler.profile=jdk7_w_errorprone +-DcommonConfig.compiler.profile=jdk7 diff --git a/README.md b/README.md index c9096b4..03720ad 100644 --- a/README.md +++ b/README.md @@ -700,8 +700,8 @@ the plugin versions as well as providing the checkstyle config rules. Specifical ## Tech Stack -* Java 7 (+ [errorprone](https://github.com/google/error-prone) static analyzer) -* Maven +* Java 7 Source, JDK 11 required to build (not yet JDK17 compatible) +* Maven 3 # Credits diff --git a/pom.xml b/pom.xml index 045e102..5803bb5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ at.favre.lib common-parent - 18 + 18.1 bytes @@ -41,7 +41,7 @@ org.apache.felix maven-bundle-plugin - 4.2.0 + 5.1.8 true @@ -71,10 +71,6 @@ org.jacoco jacoco-maven-plugin - - com.github.wvengen - proguard-maven-plugin - org.apache.maven.plugins maven-jarsigner-plugin From b1df8caa0e464382281a450cc66734362556a975 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Wed, 15 Feb 2023 19:25:47 +0100 Subject: [PATCH 20/39] Fix bugs found by sonar, mainly adding "& 0xff" to not promote int --- src/main/java/at/favre/lib/bytes/Base64.java | 2 +- src/main/java/at/favre/lib/bytes/Util.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/at/favre/lib/bytes/Base64.java b/src/main/java/at/favre/lib/bytes/Base64.java index 730c554..dd31fbf 100644 --- a/src/main/java/at/favre/lib/bytes/Base64.java +++ b/src/main/java/at/favre/lib/bytes/Base64.java @@ -93,7 +93,7 @@ static byte[] decode(CharSequence in) { } // Append this char's 6 bits to the word. - word = (word << 6) | (byte) bits; + word = (word << 6) | (byte) bits & 0xff; // For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes. inCount++; diff --git a/src/main/java/at/favre/lib/bytes/Util.java b/src/main/java/at/favre/lib/bytes/Util.java index 710c4d3..4efb673 100644 --- a/src/main/java/at/favre/lib/bytes/Util.java +++ b/src/main/java/at/favre/lib/bytes/Util.java @@ -309,7 +309,7 @@ static byte[] shiftLeft(byte[] byteArray, int shiftBitCount, ByteOrder byteOrder byte src = byteArray[sourceIndex]; byte dst = (byte) (src << shiftMod); if (sourceIndex + 1 < byteArray.length) { - dst |= byteArray[sourceIndex + 1] >>> (8 - shiftMod) & carryMask; + dst |= byteArray[sourceIndex + 1] >>> (8 - shiftMod) & carryMask & 0xff; } byteArray[i] = dst; } @@ -323,7 +323,7 @@ static byte[] shiftLeft(byte[] byteArray, int shiftBitCount, ByteOrder byteOrder byte src = byteArray[sourceIndex]; byte dst = (byte) (src << shiftMod); if (sourceIndex - 1 >= 0) { - dst |= byteArray[sourceIndex - 1] >>> (8 - shiftMod) & carryMask; + dst |= byteArray[sourceIndex - 1] >>> (8 - shiftMod) & carryMask & 0xff; } byteArray[i] = dst; } @@ -365,7 +365,7 @@ static byte[] shiftRight(byte[] byteArray, int shiftBitCount, ByteOrder byteOrde byte src = byteArray[sourceIndex]; byte dst = (byte) ((0xff & src) >>> shiftMod); if (sourceIndex - 1 >= 0) { - dst |= byteArray[sourceIndex - 1] << (8 - shiftMod) & carryMask; + dst |= byteArray[sourceIndex - 1] << (8 - shiftMod) & carryMask & 0xff; } byteArray[i] = dst; } @@ -379,7 +379,7 @@ static byte[] shiftRight(byte[] byteArray, int shiftBitCount, ByteOrder byteOrde byte src = byteArray[sourceIndex]; byte dst = (byte) ((0xff & src) >>> shiftMod); if (sourceIndex + 1 < byteArray.length) { - dst |= byteArray[sourceIndex + 1] << (8 - shiftMod) & carryMask; + dst |= byteArray[sourceIndex + 1] << (8 - shiftMod) & carryMask & 0xff; } byteArray[i] = dst; } From cb0efd0233bcc8fa180704ee18c3f492b6099a59 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Wed, 15 Feb 2023 19:41:29 +0100 Subject: [PATCH 21/39] Fix various issues mostly javadoc and typos and some simple code fixes --- .../java/at/favre/lib/bytes/BaseEncoding.java | 6 +- src/main/java/at/favre/lib/bytes/Bytes.java | 76 +++++++++---------- .../at/favre/lib/bytes/BytesTransformer.java | 8 +- .../at/favre/lib/bytes/BytesTransformers.java | 10 +-- .../at/favre/lib/bytes/BytesValidators.java | 20 ++--- .../java/at/favre/lib/bytes/MutableBytes.java | 8 +- src/main/java/at/favre/lib/bytes/Util.java | 34 ++++----- 7 files changed, 82 insertions(+), 80 deletions(-) diff --git a/src/main/java/at/favre/lib/bytes/BaseEncoding.java b/src/main/java/at/favre/lib/bytes/BaseEncoding.java index 8fe8d3e..8e23588 100644 --- a/src/main/java/at/favre/lib/bytes/BaseEncoding.java +++ b/src/main/java/at/favre/lib/bytes/BaseEncoding.java @@ -28,10 +28,10 @@ /** * Encoder which supports arbitrary alphabet and padding. - * + *

* Derived from Google Guava's common/io/ BaseEncoding *

- * See: https://github.com/google/guava/blob/v26.0/guava/src/com/google/common/io/BaseEncoding.java + * See: BaseEncoding */ final class BaseEncoding implements BinaryToTextEncoding.EncoderDecoder { private static final char ASCII_MAX = 127; @@ -190,7 +190,7 @@ char encode(int bits) { } int decode(char ch) { - return (int) decodabet[ch]; + return decodabet[ch]; } } diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index c2b5b20..33fb08d 100644 --- a/src/main/java/at/favre/lib/bytes/Bytes.java +++ b/src/main/java/at/favre/lib/bytes/Bytes.java @@ -31,7 +31,7 @@ import java.util.*; /** - * Bytes is wrapper class for an byte-array that allows a lot of convenience operations on it: + * "Bytes" is wrapper class for a byte-array that allows a lot of convenience operations on it: *

    *
  • Creation from various source: arrays, primitives, parsed or random
  • *
  • Encoding in many formats: hex, base64, etc.
  • @@ -45,7 +45,7 @@ * It supports byte ordering (little/big endianness). *

    * This class is immutable as long as the internal array is not changed from outside (which can't be assured, when - * using using wrap()). It is possible to create a mutable version (see {@link MutableBytes}). + * using wrap()). It is possible to create a mutable version (see {@link MutableBytes}). *

    * Example: *

    @@ -92,7 +92,7 @@ public static Bytes allocate(int length, byte defaultValue) {
         }
     
         /**
    -     * Creates an Byte instance with an internal empty byte array. Same as calling {@link #allocate(int)} with 0.
    +     * Creates a Byte instance with an internal empty byte array. Same as calling {@link #allocate(int)} with 0.
          *
          * @return the empty instance (always the same reference
          */
    @@ -451,7 +451,7 @@ public static Bytes from(InputStream stream) {
     
         /**
          * Reads given input stream up to maxLength and creates a new instance from read data.
    -     * Read maxLength is never longer than stream size (ie. maxLength is only limiting, not assuring maxLength)
    +     * Read maxLength is never longer than stream size (i.e. maxLength is only limiting, not assuring maxLength)
          *
          * @param stream    to read from
          * @param maxLength read to this maxLength or end of stream
    @@ -568,7 +568,7 @@ public static Bytes from(char[] charArray, Charset charset, int offset, int leng
     
         /**
          * Convert UUID to a newly generated 16 byte long array representation. Puts the 8 byte most significant bits and
    -     * 8 byte least significant bits into an byte array.
    +     * 8 byte the least significant bits into a byte array.
          *
          * @param uuid to convert to array
          * @return new instance
    @@ -611,7 +611,7 @@ public static Bytes parseDec(CharSequence decString) {
          * Encodes with given radix string representation (e.g. radix 16 would be hex).
          * See also {@link BigInteger#toString(int)}.
          * 

    - * This is usually a number encoding, not a data encoding (ie. leading zeros are not preserved), but this implementation + * This is usually a number encoding, not a data encoding (i.e. leading zeros are not preserved), but this implementation * tries to preserve the leading zeros, to keep the in/output byte length size the same, but use at your own risk! * * @param radixNumberString the encoded string @@ -628,7 +628,7 @@ public static Bytes parseRadix(CharSequence radixNumberString, int radix) { *

      *
    • Upper- and lowercase a-f (also mixed case)
    • *
    • Prefix with 0x which will be ignored
    • - *
    • Even and odd number of string length with auto zero padding (ie. 'E3F' is same as '0E3F')
    • + *
    • Even and odd number of string length with auto zero padding (i.e. 'E3F' is same as '0E3F')
    • *
    * * @param hexString the encoded string @@ -654,7 +654,7 @@ public static Bytes parseBase32(CharSequence base32Rfc4648String) { /** * Parsing of base36 encoded byte arrays. *

    - * This is usually a number encoding, not a data encoding (ie. leading zeros are not preserved), but this implementation + * This is usually a number encoding, not a data encoding (i.e. leading zeros are not preserved), but this implementation * tries to preserve the leading zeros, to keep the in/output byte length size the same. * * @param base36String the encoded string @@ -766,7 +766,7 @@ public static Bytes random(int length, Random random) { /* TRANSFORMER **********************************************************************************************/ /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end). + * Creates a new instance with the current array appended to the provided data (i.e. append at the end). *

    * This will create a new byte array internally, so it is not suitable to use as extensive builder pattern - * use {@link ByteBuffer} or {@link java.io.ByteArrayOutputStream} for that. @@ -779,7 +779,7 @@ public Bytes append(Bytes bytes) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end) + * Creates a new instance with the current array appended to the provided data (i.e. append at the end) * * @param singleByte to append * @return appended instance @@ -789,7 +789,7 @@ public Bytes append(byte singleByte) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end) + * Creates a new instance with the current array appended to the provided data (i.e. append at the end) * * @param char2Bytes to append * @return appended instance @@ -799,7 +799,7 @@ public Bytes append(char char2Bytes) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end) + * Creates a new instance with the current array appended to the provided data (i.e. append at the end) * * @param short2Bytes to append * @return appended instance @@ -809,7 +809,7 @@ public Bytes append(short short2Bytes) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end) + * Creates a new instance with the current array appended to the provided data (i.e. append at the end) * * @param integer4Bytes to append * @return appended instance @@ -819,7 +819,7 @@ public Bytes append(int integer4Bytes) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end) + * Creates a new instance with the current array appended to the provided data (i.e. append at the end) * * @param long8Bytes to append * @return appended instance @@ -829,7 +829,7 @@ public Bytes append(long long8Bytes) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end). + * Creates a new instance with the current array appended to the provided data (i.e. append at the end). * You may use this to append multiple byte arrays without the need for chaining the {@link #append(byte[])} call * and therefore generating intermediate copies of the byte array, making this approach use less memory. * @@ -841,7 +841,7 @@ public Bytes append(byte[]... arrays) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end) + * Creates a new instance with the current array appended to the provided data (i.e. append at the end) * * @param secondArray to append * @return appended instance @@ -851,7 +851,7 @@ public Bytes append(byte[] secondArray) { } /** - * Creates a new instance with the current array appended to the provided data (ie. append at the end) + * Creates a new instance with the current array appended to the provided data (i.e. append at the end) *

    * If given array is null, the nothing will be appended. * @@ -1004,7 +1004,7 @@ public Bytes rightShift(int shiftCount) { } /** - * Returns a Byte whose value is equivalent to this Byte with the designated bit set to newBitValue. Bits start to count from the LSB (ie. Bytes.from(0).switchBit(0,true) == 1) + * Returns a Byte whose value is equivalent to this Byte with the designated bit set to newBitValue. Bits start to count from the LSB (i.e. Bytes.from(0).switchBit(0,true) == 1) * * @param bitPosition not to confuse with byte position * @param newBitValue if true set to 1, 0 otherwise @@ -1063,7 +1063,7 @@ public Bytes reverse() { * copy but not the original, the copy will contain {@code (byte)0}. *

    * Resize from LSB or length, so an array [0,1,2,3] resized to 3 will result in [1,2,3] or resized to 5 [0,0,1,2,3]. - * So when a 8 byte value resized to 4 byte will result in the same 32 bit integer value + * So when an 8 byte value resized to 4 byte will result in the same 32-bit integer value * * @param newByteLength the length of the copy to be returned * @return a copy with the desired size or "this" instance if newByteLength == current length @@ -1099,7 +1099,7 @@ public Bytes resize(int newByteLength, BytesTransformer.ResizeTransformer.Mode m * Calculates md5 on the underlying byte array and returns a byte instance containing the hash. * This hash algorithm SHOULD be supported by every JVM implementation (see * Javadoc for MessageDigest) - * + *

    * Do not use this algorithm in security relevant applications. * * @return md5 (16 bytes) hash of internal byte array @@ -1114,7 +1114,7 @@ public Bytes hashMd5() { * Calculates sha1 on the underlying byte array and returns a byte instance containing the hash. * This hash algorithm SHOULD be supported by every JVM implementation (see * Javadoc for MessageDigest) - * + *

    * Do not use this algorithm in security relevant applications. * * @return sha1 (20 bytes) hash of internal byte array @@ -1151,9 +1151,9 @@ public Bytes hash(String algorithm) { /** * Generic transformation of this instance. *

    - * This transformation might be done in-place (ie. without copying the internal array and overwriting its old state), + * This transformation might be done in-place (i.e. without copying the internal array and overwriting its old state), * or on a copy of the internal data, depending on the type (e.g. {@link MutableBytes}) and if the operation can be done - * in-place. Therefore the caller has to ensure that certain side-effects, which occur due to the changing of the internal + * in-place. Therefore, the caller has to ensure that certain side effects, which occur due to the changing of the internal * data, do not create bugs in his/her code. Usually immutability is preferred, but when handling many or big byte arrays, * mutability enables drastically better performance. * @@ -1199,7 +1199,7 @@ public int length() { /** * The bit length of the underlying byte array. * - * @return bit length + * @return the bit length */ public int lengthBit() { return length() * 8; @@ -1227,7 +1227,7 @@ public ByteOrder byteOrder() { /** * Checks if instance is mutable * - * @return true if mutable, ie. transformers will change internal array + * @return true if mutable, i.e. transformers will change internal array */ public boolean isMutable() { return false; @@ -1384,7 +1384,7 @@ public boolean endsWith(byte[] subArray) { * 1000 0000 has bitAt(0) == false and bitAt(7) == true. * * @param bitIndex the index of the {@code bit} value. - * @return true if bit at given index is set, false otherwise + * @return true if the bit at given index is set, false otherwise * @throws IndexOutOfBoundsException if the {@code bitIndex} argument is negative or not less than the length of this array in bits. */ public boolean bitAt(int bitIndex) { @@ -1434,7 +1434,7 @@ public int unsignedByteAt(int index) { */ public char charAt(int index) { Util.Validation.checkIndexBounds(length(), index, 2, "char"); - return ((ByteBuffer) internalBuffer().position(index)).getChar(); + return internalBuffer().position(index).getChar(); } /** @@ -1447,7 +1447,7 @@ public char charAt(int index) { */ public short shortAt(int index) { Util.Validation.checkIndexBounds(length(), index, 2, "short"); - return ((ByteBuffer) internalBuffer().position(index)).getShort(); + return internalBuffer().position(index).getShort(); } /** @@ -1460,7 +1460,7 @@ public short shortAt(int index) { */ public int intAt(int index) { Util.Validation.checkIndexBounds(length(), index, 4, "int"); - return ((ByteBuffer) internalBuffer().position(index)).getInt(); + return internalBuffer().position(index).getInt(); } /** @@ -1473,7 +1473,7 @@ public int intAt(int index) { */ public long longAt(int index) { Util.Validation.checkIndexBounds(length(), index, 8, "long"); - return ((ByteBuffer) internalBuffer().position(index)).getLong(); + return internalBuffer().position(index).getLong(); } /** @@ -1606,7 +1606,7 @@ public InputStream inputStream() { /** * The reference of te internal byte-array. This call requires no conversation or additional memory allocation. *

    - * Modifications to this bytes's content will cause the returned + * Modifications to these byte's content will cause the returned * array's content to be modified, and vice versa. * * @return the direct reference of the internal byte array @@ -1662,12 +1662,12 @@ public String encodeDec() { /** * Encodes the internal array in given radix representation (e.g. 2 = binary, 10 = decimal, 16 = hex). *

    - * This is usually a number encoding, not a data encoding (ie. leading zeros are not preserved), but this implementation + * This is usually a number encoding, not a data encoding (i.e. leading zeros are not preserved), but this implementation * tries to preserve the leading zeros, to keep the in/output byte length size the same. To preserve the length padding * would be required, but is not supported in this implementation. *

    * But still full disclaimer: - * + *

    * This is NOT recommended for data encoding, only for number encoding *

    * See Radix Economy and {@link BigInteger#toString(int)}. @@ -1868,7 +1868,7 @@ public BitSet toBitSet() { * The internal byte array wrapped in a {@link BigInteger} instance. *

    * If the internal byte order is {@link ByteOrder#LITTLE_ENDIAN}, a copy of the internal - * array will be reversed and used as backing array with the big integer. Otherwise the internal + * array will be reversed and used as backing array with the big integer. Otherwise, the internal * array will be used directly. * * @return big integer @@ -2005,7 +2005,7 @@ public long toLong() { } /** - * Converts the internal byte array to an long array, that is, every 8 bytes will be packed into a single long. + * Converts the internal byte array to a long array, that is, every 8 bytes will be packed into a single long. *

    * E.g. 8 bytes will be packed to a length 1 long array: *

    @@ -2037,7 +2037,7 @@ public float toFloat() {
         }
     
         /**
    -     * Converts the internal byte array to an float array, that is, every 4 bytes will be packed into a single float.
    +     * Converts the internal byte array to a float array, that is, every 4 bytes will be packed into a single float.
          * 

    * E.g. 4 bytes will be packed to a length 1 float array: *

    @@ -2069,7 +2069,7 @@ public double toDouble() {
         }
     
         /**
    -     * Converts the internal byte array to an double array, that is, every 8 bytes will be packed into a single double.
    +     * Converts the internal byte array to a double array, that is, every 8 bytes will be packed into a single double.
          * 

    * E.g. 8 bytes will be packed to a length 1 double array: *

    @@ -2178,7 +2178,7 @@ public boolean equals(byte[] anotherArray) {
          * will not break on the first mismatch. This method is useful to prevent some side-channel attacks,
          * but is slower on average.
          * 

    - * This implementation uses the algorithm suggested in https://codahale.com/a-lesson-in-timing-attacks/ + * This implementation uses the algorithm suggested in a-lesson-in-timing-attacks * * @param anotherArray to compare with * @return true if {@link Arrays#equals(byte[], byte[])} returns true on given and internal array diff --git a/src/main/java/at/favre/lib/bytes/BytesTransformer.java b/src/main/java/at/favre/lib/bytes/BytesTransformer.java index 22367d8..efdd12c 100644 --- a/src/main/java/at/favre/lib/bytes/BytesTransformer.java +++ b/src/main/java/at/favre/lib/bytes/BytesTransformer.java @@ -231,7 +231,7 @@ public boolean supportInPlaceTransformation() { * contain identical values. For any indices that are valid in the * copy but not the original, the copy will contain {@code (byte)0}. *

    - * If if the internal array will be grown, zero bytes will be added on the left, + * If the internal array will be increased, zero bytes will be added on the left, * keeping the value the same. */ final class ResizeTransformer implements BytesTransformer { @@ -265,10 +265,12 @@ public byte[] transform(byte[] currentArray, boolean inPlace) { byte[] resizedArray = new byte[newSize]; if (mode == Mode.RESIZE_KEEP_FROM_MAX_LENGTH) { + int max = Math.max(0, Math.abs(newSize - currentArray.length)); + if (newSize > currentArray.length) { - System.arraycopy(currentArray, 0, resizedArray, Math.max(0, Math.abs(newSize - currentArray.length)), Math.min(newSize, currentArray.length)); + System.arraycopy(currentArray, 0, resizedArray, max, currentArray.length); } else { - System.arraycopy(currentArray, Math.max(0, Math.abs(newSize - currentArray.length)), resizedArray, Math.min(0, Math.abs(newSize - currentArray.length)), Math.min(newSize, currentArray.length)); + System.arraycopy(currentArray, max, resizedArray, 0, newSize); } } else { System.arraycopy(currentArray, 0, resizedArray, 0, Math.min(currentArray.length, resizedArray.length)); diff --git a/src/main/java/at/favre/lib/bytes/BytesTransformers.java b/src/main/java/at/favre/lib/bytes/BytesTransformers.java index fc448c9..c1914c0 100644 --- a/src/main/java/at/favre/lib/bytes/BytesTransformers.java +++ b/src/main/java/at/favre/lib/bytes/BytesTransformers.java @@ -44,7 +44,7 @@ public static BytesTransformer shuffle(Random random) { } /** - * Create a {@link BytesTransformer} which sorts the internal byte array with it's natural ordering treating + * Create a {@link BytesTransformer} which sorts the internal byte array with its natural ordering treating * each byte as signed byte (-128...127). Using inplace sorting, this can be reasonable fast. * * @return transformer @@ -54,9 +54,9 @@ public static BytesTransformer sort() { } /** - * Create a {@link BytesTransformer} which sorts the internal byte array with it's natural ordering treating + * Create a {@link BytesTransformer} which sorts the internal byte array with its natural ordering treating * each byte as unsigned byte (0...255). That is, the byte string {@code ff} sorts after {@code 00}. - * + *

    * Note: this requires 2 copies of the internal array and a lot of unboxing due to * the fact that no primitives are not allowed as generic type arguments - so only use on small arrays. * @@ -68,7 +68,7 @@ public static BytesTransformer sortUnsigned() { /** * Create a {@link BytesTransformer} which sorts the internal byte array according to given comparator. - * + *

    * Note: this requires 2 copies of the internal array and a lot of unboxing due to * the fact that no primitives are not allowed as generic type arguments - so only use on small arrays. * @@ -192,7 +192,7 @@ public boolean supportInPlaceTransformation() { * Sorts the internal byte array with given {@link java.util.Comparator} */ public static final class SortTransformer implements BytesTransformer { - private final Comparator comparator; + private final Comparator comparator; SortTransformer() { this(null); diff --git a/src/main/java/at/favre/lib/bytes/BytesValidators.java b/src/main/java/at/favre/lib/bytes/BytesValidators.java index acc2104..0d0f30f 100644 --- a/src/main/java/at/favre/lib/bytes/BytesValidators.java +++ b/src/main/java/at/favre/lib/bytes/BytesValidators.java @@ -37,7 +37,7 @@ private BytesValidators() { * Checks the length of a byte array * * @param byteLength to check against - * @return true if longer or equal to given value + * @return validator that returns true if longer or equal to given value */ public static BytesValidator atLeast(int byteLength) { return new BytesValidator.Length(byteLength, BytesValidator.Length.Mode.GREATER_OR_EQ_THAN); @@ -47,7 +47,7 @@ public static BytesValidator atLeast(int byteLength) { * Checks the length of a byte array * * @param byteLength to check against - * @return true if smaller or equal to given value + * @return validator that returns true if smaller or equal to given value */ public static BytesValidator atMost(int byteLength) { return new BytesValidator.Length(byteLength, BytesValidator.Length.Mode.SMALLER_OR_EQ_THAN); @@ -57,7 +57,7 @@ public static BytesValidator atMost(int byteLength) { * Checks the length of a byte array * * @param byteLength to check against - * @return true if equal to given value + * @return validator that returns true if equal to given value */ public static BytesValidator exactLength(int byteLength) { return new BytesValidator.Length(byteLength, BytesValidator.Length.Mode.EXACT); @@ -67,7 +67,7 @@ public static BytesValidator exactLength(int byteLength) { * Checks individual byte content * * @param refByte to check against - * @return true if array only consists of refByte + * @return validator that returns true if array only consists of refByte */ public static BytesValidator onlyOf(byte refByte) { return new BytesValidator.IdenticalContent(refByte, BytesValidator.IdenticalContent.Mode.ONLY_OF); @@ -77,7 +77,7 @@ public static BytesValidator onlyOf(byte refByte) { * Checks individual byte content * * @param refByte to check against - * @return true if array has at least one byte that is not refByte + * @return validator that returns true if array has at least one byte that is not refByte */ public static BytesValidator notOnlyOf(byte refByte) { return new BytesValidator.IdenticalContent(refByte, BytesValidator.IdenticalContent.Mode.NOT_ONLY_OF); @@ -87,7 +87,7 @@ public static BytesValidator notOnlyOf(byte refByte) { * Checks if the internal byte array starts with given bytes * * @param startsWithBytes the supposed prefix - * @return true all startsWithBytes match the first bytes in the internal array + * @return validator that returns true all startsWithBytes match the first bytes in the internal array */ public static BytesValidator startsWith(byte... startsWithBytes) { return new BytesValidator.PrePostFix(true, startsWithBytes); @@ -97,7 +97,7 @@ public static BytesValidator startsWith(byte... startsWithBytes) { * Checks if the internal byte array ends with given bytes * * @param endsWithBytes the supposed postfix - * @return true all startsWithBytes match the first bytes in the internal array + * @return validator that returns true all startsWithBytes match the first bytes in the internal array */ public static BytesValidator endsWith(byte... endsWithBytes) { return new BytesValidator.PrePostFix(false, endsWithBytes); @@ -107,7 +107,7 @@ public static BytesValidator endsWith(byte... endsWithBytes) { * Checks individual byte content * * @param refByte to check against - * @return true if array has no value refByte + * @return validator that returns true if array has no value refByte */ public static BytesValidator noneOf(byte refByte) { return new BytesValidator.IdenticalContent(refByte, BytesValidator.IdenticalContent.Mode.NONE_OF); @@ -117,7 +117,7 @@ public static BytesValidator noneOf(byte refByte) { * This will execute all passed validators and returns true if at least one returns true (i.e. OR concatenation) * * @param validators at least one validator must be passed - * @return true if at least one validator returns true + * @return validator that returns true if at least one validator returns true */ public static BytesValidator or(BytesValidator... validators) { return new BytesValidator.Logical(Arrays.asList(validators), BytesValidator.Logical.Operator.OR); @@ -127,7 +127,7 @@ public static BytesValidator or(BytesValidator... validators) { * This will execute all passed validators and returns true if all return true (i.e. AND concatenation) * * @param validators at least one validator must be passed - * @return true if all return true + * @return validator that returns true if all return true */ public static BytesValidator and(BytesValidator... validators) { return new BytesValidator.Logical(Arrays.asList(validators), BytesValidator.Logical.Operator.AND); diff --git a/src/main/java/at/favre/lib/bytes/MutableBytes.java b/src/main/java/at/favre/lib/bytes/MutableBytes.java index 530f002..270ae29 100644 --- a/src/main/java/at/favre/lib/bytes/MutableBytes.java +++ b/src/main/java/at/favre/lib/bytes/MutableBytes.java @@ -69,7 +69,7 @@ public boolean isMutable() { * * @param newArray used to overwrite internal * @return this instance - * @throws IndexOutOfBoundsException if newArray.length > internal length + * @throws IndexOutOfBoundsException if newArray.length() > internal length */ public MutableBytes overwrite(byte[] newArray) { return overwrite(newArray, 0); @@ -80,7 +80,7 @@ public MutableBytes overwrite(byte[] newArray) { * * @param newBytes used to overwrite internal * @return this instance - * @throws IndexOutOfBoundsException if newArray.length > internal length + * @throws IndexOutOfBoundsException if newArray.length() > internal length */ public MutableBytes overwrite(Bytes newBytes) { return overwrite(newBytes, 0); @@ -92,7 +92,7 @@ public MutableBytes overwrite(Bytes newBytes) { * @param newArray used to overwrite internal * @param offsetInternalArray index of the internal array to start overwriting * @return this instance - * @throws IndexOutOfBoundsException if newArray.length + offsetInternalArray > internal length + * @throws IndexOutOfBoundsException if newArray.length() + offsetInternalArray > internal length */ public MutableBytes overwrite(byte[] newArray, int offsetInternalArray) { Objects.requireNonNull(newArray, "must provide non-null array as source"); @@ -106,7 +106,7 @@ public MutableBytes overwrite(byte[] newArray, int offsetInternalArray) { * @param newBytes used to overwrite internal * @param offsetInternalArray index of the internal array to start overwriting * @return this instance - * @throws IndexOutOfBoundsException if newBytes.length + offsetInternalArray > internal length + * @throws IndexOutOfBoundsException if newBytes.length() + offsetInternalArray > internal length */ public MutableBytes overwrite(Bytes newBytes, int offsetInternalArray) { return overwrite(Objects.requireNonNull(newBytes, "must provide non-null array as source").array(), offsetInternalArray); diff --git a/src/main/java/at/favre/lib/bytes/Util.java b/src/main/java/at/favre/lib/bytes/Util.java index 4efb673..ac234bb 100644 --- a/src/main/java/at/favre/lib/bytes/Util.java +++ b/src/main/java/at/favre/lib/bytes/Util.java @@ -163,7 +163,7 @@ static int lastIndexOf(byte[] array, byte target, int start, int end) { } /** - * Counts the occurrence of target in the the in the subject array + * Counts the occurrence of target in the subject array * *

    * Analysis @@ -189,7 +189,7 @@ static int countByte(byte[] array, byte target) { } /** - * Counts the times given pattern (ie. an array) can be found in given array + * Counts the times given pattern (i.e. an array) can be found in given array * *

    * Analysis @@ -226,7 +226,7 @@ static int countByteArray(byte[] array, byte[] pattern) { * Simple Durstenfeld shuffle. * This will shuffle given array and will not make a copy, so beware. *

    - * See: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm + * See: Yates_shuffle * *

    * Analysis @@ -418,7 +418,7 @@ static boolean constantTimeEquals(byte[] array, byte[] anotherArray) { * Calculates the entropy factor of a byte array. *

    * This implementation will not create a copy of the internal array and will only internally initialize - * a int array with 256 elements as temporary buffer. + * an int array with 256 elements as temporary buffer. * *

    * Analysis @@ -496,7 +496,7 @@ static byte[] toArray(Collection collection) { } /** - * Converts this primitive array to an boxed object array. + * Converts this primitive array to a boxed object array. * Will create a new array and not reuse the array reference. * *

    @@ -544,7 +544,7 @@ static List toList(byte[] array) { } /** - * Converts this object array to an primitives type array. + * Converts this object array to a primitives type array. * Will create a new array and not reuse the array reference. * *

    @@ -717,7 +717,7 @@ static byte[] toByteArray(double[] doubleArray) { * @param charArray to get the byte array from * @param charset charset to be used to decode the char array * @param offset to start reading the char array from (must be smaller than length and gt 0) - * @param length from offset (must be between 0 and charArray.length) + * @param length from offset (must be between 0 and charArray.length()) * @return byte array of encoded chars */ static byte[] charToByteArray(char[] charArray, Charset charset, int offset, int length) { @@ -807,7 +807,7 @@ static int[] toIntArray(byte[] bytes, ByteOrder byteOrder) { } /** - * Converts the byte array to an long array. This will spread 8 bytes into a single long: + * Converts the byte array to a long array. This will spread 8 bytes into a single long: * *

              *     [b1, b2, b3, b4, b5, b6, b7, b8] = [long1]
    @@ -834,7 +834,7 @@ static long[] toLongArray(byte[] bytes, ByteOrder byteOrder) {
             }
     
             /**
    -         * Converts the byte array to an float array. This will spread 4 bytes into a single float:
    +         * Converts the byte array to a float array. This will spread 4 bytes into a single float:
              *
              * 
              *     [b1, b2, b3, b4] = [float1]
    @@ -861,7 +861,7 @@ static float[] toFloatArray(byte[] bytes, ByteOrder byteOrder) {
             }
     
             /**
    -         * Converts the byte array to an double array. This will spread 8 bytes into a single double:
    +         * Converts the byte array to a double array. This will spread 8 bytes into a single double:
              *
              * 
              *     [b1, b2, b3, b4, b5, b6, b7, b8] = [double1]
    @@ -916,7 +916,7 @@ static short[] toShortArray(byte[] bytes, ByteOrder byteOrder) {
     
             /**
              * Convert UUID to a newly generated 16 byte long array representation. Puts the 8 byte most significant bits and
    -         * 8 byte least significant bits into an byte array.
    +         * 8 byte least-significant bits into a byte array.
              *
              * 

    * Analysis @@ -1034,13 +1034,13 @@ private Validation() { } /** - * Check if a length of an primitive (e.g. int = 4 byte) fits in given length from given start index. + * Check if a length of a primitive (e.g. int = 4 byte) fits in given length from given start index. * Throws exception with descriptive exception message. * * @param length of the whole array * @param index to start from array length * @param primitiveLength length of the primitive type to check - * @param type for easier debugging the human readable type of the checked primitive + * @param type for easier debugging the human-readable type of the checked primitive * to put in exception message * @throws IndexOutOfBoundsException if index + primitiveLength > length */ @@ -1056,7 +1056,7 @@ static void checkIndexBounds(int length, int index, int primitiveLength, String * * @param length of the whole array * @param expectedLength how length is expected - * @param type for easier debugging the human readable type of the checked primitive + * @param type for easier debugging the human-readable type of the checked primitive * to put in exception message * @throws IllegalArgumentException if length != expectedLength */ @@ -1073,7 +1073,7 @@ static void checkExactLength(int length, int expectedLength, String type) { * * @param length of the byte array * @param modFactor to divide the length - * @param errorSubject human readable message of the exact error subject + * @param errorSubject human-readable message of the exact error subject * @throws IllegalArgumentException if length % modFactor != 0 */ static void checkModLength(int length, int modFactor, String errorSubject) { @@ -1086,7 +1086,7 @@ static void checkModLength(int length, int modFactor, String errorSubject) { * Check if the file exists and is a file. * * @param file to check - * @throws IllegalArgumentException if either file is null, does not exists or is not a file + * @throws IllegalArgumentException if either file is null, does not exist or is not a file */ private static void checkFileExists(java.io.File file) { if (file == null || !file.exists() || !file.isFile()) { @@ -1181,7 +1181,7 @@ static byte[] readFromFile(java.io.File file) { * @param file to read bytes from * @param offset to read * @param length from offset - * @return byte array with length length + * @return byte array with length */ static byte[] readFromFile(java.io.File file, int offset, int length) { Validation.checkFileExists(file); From 9644a8d432bb1ed5aa074dea4a0ad47bc042048d Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Wed, 15 Feb 2023 19:49:17 +0100 Subject: [PATCH 22/39] Bump Version --- CHANGELOG | 7 +++++++ pom.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 7cdb7ad..ee53874 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,12 @@ # Releases +## v1.6.1 + +* now build by JDK 11 and removed errorprone compiler #52 +* introduce sonarqube and remove codecov +* improve javadoc by fixing many typos #53 +* some small bugfixes + ## v1.6.0 * migrate to github actions, codecov and maven central #49 diff --git a/pom.xml b/pom.xml index 5803bb5..c6a31a8 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ bytes - 1.6.0 + 1.6.1 bundle Bytes Utility Library From 68344d8b6c0247592b7f5e313cf4ff3f1cecc3ce Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Wed, 15 Feb 2023 22:58:22 +0100 Subject: [PATCH 23/39] Improve github actions script --- .github/workflows/build_deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index 607a0ca..bd8642a 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -39,7 +39,7 @@ jobs: java-version: '11' distribution: 'temurin' - name: Build with Maven - run: ./mvnw -B clean verify checkstyle:checkstyle jacoco:report -DcommonConfig.jarSign.skip=true + run: ./mvnw -B clean verify -DcommonConfig.jarSign.skip=true - name: Analyze with SonaQube env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any @@ -77,7 +77,7 @@ jobs: gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Publish package run: | - ./mvnw -B verify nexus-staging:deploy -P deploy && \ + ./mvnw -B verify nexus-staging:deploy -P deploy -DskipTests && \ ./mvnw -B nexus-staging:release -P deploy env: OPENSOURCE_PROJECTS_KS_PW: ${{ secrets.KEYSTORE_PASSWORD }} From 71b8daad0c3e7a011ba9a91acf6ddce8eb20e495 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Thu, 2 Mar 2023 20:24:08 +0100 Subject: [PATCH 24/39] Create dependabot.yml --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..90e2fb4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +# dependabot analyzing maven dependencies +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + open-pull-requests-limit: 3 + schedule: + interval: "weekly" + labels: + - "dependencies" From f0c83d4fc364a03d79291efaca7373d79f721d49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Mar 2023 19:24:28 +0000 Subject: [PATCH 25/39] Bump jmh-core from 1.35 to 1.36 Bumps [jmh-core](https://github.com/openjdk/jmh) from 1.35 to 1.36. - [Release notes](https://github.com/openjdk/jmh/releases) - [Commits](https://github.com/openjdk/jmh/compare/1.35...1.36) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6a31a8..9177165 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ org.openjdk.jmh jmh-core - 1.35 + 1.36 test From 8db53b89596516f0343e856f55d507e0100ebbdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Mar 2023 19:24:32 +0000 Subject: [PATCH 26/39] Bump common-parent from 18.1 to 19 Bumps [common-parent](https://github.com/patrickfav/mvn-common-parent) from 18.1 to 19. - [Release notes](https://github.com/patrickfav/mvn-common-parent/releases) - [Changelog](https://github.com/patrickfav/mvn-common-parent/blob/main/CHANGELOG) - [Commits](https://github.com/patrickfav/mvn-common-parent/compare/v18.1...v19) --- updated-dependencies: - dependency-name: at.favre.lib:common-parent dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6a31a8..0082b22 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ at.favre.lib common-parent - 18.1 + 19 bytes From 4dfb09c05753cb51289ccd594a9e7d027049ce62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Mar 2023 19:24:37 +0000 Subject: [PATCH 27/39] Bump jmh-generator-annprocess from 1.35 to 1.36 Bumps [jmh-generator-annprocess](https://github.com/openjdk/jmh) from 1.35 to 1.36. - [Release notes](https://github.com/openjdk/jmh/releases) - [Commits](https://github.com/openjdk/jmh/compare/1.35...1.36) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6a31a8..d4fadd6 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.35 + 1.36 test From 2bc552d85fb8da60009951261d0f3d0f76fc348a Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Thu, 2 Mar 2023 20:44:22 +0100 Subject: [PATCH 28/39] Ignore SonarQube CI Step on dependabot --- .github/workflows/build_deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index bd8642a..47ea839 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -41,6 +41,7 @@ jobs: - name: Build with Maven run: ./mvnw -B clean verify -DcommonConfig.jarSign.skip=true - name: Analyze with SonaQube + if: ${{ github.actor != 'dependabot[bot]' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 0bf1b6089403f0db35a94db9adb2dbd3c5377173 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 10:56:34 +0000 Subject: [PATCH 29/39] Bump common-parent from 19 to 20 Bumps [common-parent](https://github.com/patrickfav/mvn-common-parent) from 19 to 20. - [Release notes](https://github.com/patrickfav/mvn-common-parent/releases) - [Changelog](https://github.com/patrickfav/mvn-common-parent/blob/main/CHANGELOG) - [Commits](https://github.com/patrickfav/mvn-common-parent/compare/v19...v20) --- updated-dependencies: - dependency-name: at.favre.lib:common-parent dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc84494..1c8a255 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ at.favre.lib common-parent - 19 + 20 bytes From 165118b84c3dd84859d7ef628879bf800ba8021b Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sun, 12 Mar 2023 19:19:54 +0100 Subject: [PATCH 30/39] Update url in pom --- pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 1c8a255..b7d7083 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ arrays in Java. It supports endianness as well as immutability and mutability, so the caller may decide to favor performance. - https://github.com/patrickfav/bytes-java + https://favr.dev/opensource/bytes-java 2017 @@ -82,11 +82,11 @@ maven-jar-plugin - - - at.favre.lib.bytes - - + + + at.favre.lib.bytes + + From b54f29249310a00731b46d519555ac220e82cda6 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sun, 26 Mar 2023 18:03:06 +0200 Subject: [PATCH 31/39] Bump version to 1.6.2 --- CHANGELOG | 4 ++++ pom.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ee53874..e30ed69 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ # Releases +## 1.6.2 + +* remove hashCode caching since it could introduce very subtle bugs + ## v1.6.1 * now build by JDK 11 and removed errorprone compiler #52 diff --git a/pom.xml b/pom.xml index b7d7083..cd65912 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ bytes - 1.6.1 + 1.6.2 bundle Bytes Utility Library From 509644df1b0db73fd8a75d0662d2bd0d84cefd35 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sun, 26 Mar 2023 18:03:13 +0200 Subject: [PATCH 32/39] Remove hashCode caching since it could introduce very subtle bugs --- src/main/java/at/favre/lib/bytes/Bytes.java | 6 +----- src/main/java/at/favre/lib/bytes/MutableBytes.java | 5 ----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index 33fb08d..123eeec 100644 --- a/src/main/java/at/favre/lib/bytes/Bytes.java +++ b/src/main/java/at/favre/lib/bytes/Bytes.java @@ -745,7 +745,6 @@ public static Bytes random(int length, Random random) { private final byte[] byteArray; private final ByteOrder byteOrder; private final BytesFactory factory; - private transient int hashCodeCache; Bytes(byte[] byteArray, ByteOrder byteOrder) { this(byteArray, byteOrder, new Factory()); @@ -2221,10 +2220,7 @@ public boolean equalsContent(Bytes other) { @Override public int hashCode() { - if (hashCodeCache == 0) { - hashCodeCache = Util.Obj.hashCode(internalArray(), byteOrder()); - } - return hashCodeCache; + return Util.Obj.hashCode(internalArray(), byteOrder()); } /** diff --git a/src/main/java/at/favre/lib/bytes/MutableBytes.java b/src/main/java/at/favre/lib/bytes/MutableBytes.java index 270ae29..dbc3606 100644 --- a/src/main/java/at/favre/lib/bytes/MutableBytes.java +++ b/src/main/java/at/favre/lib/bytes/MutableBytes.java @@ -178,11 +178,6 @@ public Bytes immutable() { return Bytes.wrap(internalArray(), byteOrder()); } - @Override - public int hashCode() { - return Util.Obj.hashCode(internalArray(), byteOrder()); - } - @Override public boolean equals(Object o) { return super.equals(o); From 3e8216313d98e708cefa06a93dedddc17967213f Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sun, 26 Mar 2023 18:03:32 +0200 Subject: [PATCH 33/39] Update Jmh benchmark with JDK17 emulation --- .../lib/bytes/EncodingHexJmhBenchmark.java | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/src/test/java/at/favre/lib/bytes/EncodingHexJmhBenchmark.java b/src/test/java/at/favre/lib/bytes/EncodingHexJmhBenchmark.java index e079f2e..80e297b 100644 --- a/src/test/java/at/favre/lib/bytes/EncodingHexJmhBenchmark.java +++ b/src/test/java/at/favre/lib/bytes/EncodingHexJmhBenchmark.java @@ -23,10 +23,13 @@ import org.openjdk.jmh.annotations.*; +import java.io.IOException; import java.math.BigInteger; import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -128,6 +131,7 @@ public class EncodingHexJmhBenchmark { private BinaryToTextEncoding.EncoderDecoder option3; private BinaryToTextEncoding.EncoderDecoder option4; private BinaryToTextEncoding.EncoderDecoder option5; + private BinaryToTextEncoding.EncoderDecoder option6; private Random random; @Setup(Level.Trial) @@ -139,6 +143,7 @@ public void setup() { option3 = new BigIntegerHexEncoder(); option4 = new OldBytesImplementation(); option5 = new StackOverflowAnswer2Encoder(); + option6 = new Jdk17HexFormat(); rndMap = new HashMap<>(); int[] lengths = new int[]{4, 8, 16, 32, 128, 512, 1000000}; @@ -176,6 +181,11 @@ public String encodeStackOverflowCode2() { return encodeDecode(option5); } + @Benchmark + public String encodeHexFormatJdk17() { + return encodeDecode(option6); + } + private String encodeDecode(BinaryToTextEncoding.EncoderDecoder encoder) { Bytes[] bytes = rndMap.get(byteLength); int rndNum = random.nextInt(bytes.length); @@ -285,4 +295,140 @@ public byte[] decode(CharSequence encoded) { throw new UnsupportedOperationException(); } } + + /** + * Copy of the JDK 17 implementation of HexFormat, only difference is that we need to create new strings, while + * the JDK can create strings without byte copy, I cant here. + */ + static final class Jdk17HexFormat implements BinaryToTextEncoding.EncoderDecoder { + private static final byte[] LOWERCASE_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + }; + private final String delimiter = ""; + private final String prefix = ""; + private final String suffix = ""; + private final byte[] digits = LOWERCASE_DIGITS; + + @Override + public String encode(byte[] byteArray, ByteOrder byteOrder) { + return formatHex(byteArray, 0, byteArray.length); + } + + private String formatHex(byte[] bytes, int fromIndex, int toIndex) { + Objects.requireNonNull(bytes, "bytes"); + //Objects.checkFromToIndex(fromIndex, toIndex, bytes.length); + if (toIndex - fromIndex == 0) { + return ""; + } + // Format efficiently if possible + String s = formatOptDelimiter(bytes, fromIndex, toIndex); + if (s == null) { + long stride = prefix.length() + 2L + suffix.length() + delimiter.length(); + int capacity = checkMaxArraySize((toIndex - fromIndex) * stride - delimiter.length()); + StringBuilder sb = new StringBuilder(capacity); + formatHex(sb, bytes, fromIndex, toIndex); + s = sb.toString(); + } + return s; + } + + private A formatHex(A out, byte[] bytes, int fromIndex, int toIndex) { + Objects.requireNonNull(out, "out"); + Objects.requireNonNull(bytes, "bytes"); + //Objects.checkFromToIndex(fromIndex, toIndex, bytes.length); + + int length = toIndex - fromIndex; + if (length > 0) { + try { + String between = suffix + delimiter + prefix; + out.append(prefix); + toHexDigits(out, bytes[fromIndex]); + if (between.isEmpty()) { + for (int i = 1; i < length; i++) { + toHexDigits(out, bytes[fromIndex + i]); + } + } else { + for (int i = 1; i < length; i++) { + out.append(between); + toHexDigits(out, bytes[fromIndex + i]); + } + } + out.append(suffix); + } catch (IOException ioe) { + throw new RuntimeException(ioe.getMessage(), ioe); + } + } + return out; + } + + private A toHexDigits(A out, byte value) { + Objects.requireNonNull(out, "out"); + try { + out.append(toHighHexDigit(value)); + out.append(toLowHexDigit(value)); + return out; + } catch (IOException ioe) { + throw new RuntimeException(ioe.getMessage(), ioe); + } + } + + private String formatOptDelimiter(byte[] bytes, int fromIndex, int toIndex) { + byte[] rep; + if (!prefix.isEmpty() || !suffix.isEmpty()) { + return null; + } + int length = toIndex - fromIndex; + if (delimiter.isEmpty()) { + // Allocate the byte array and fill in the hex pairs for each byte + rep = new byte[checkMaxArraySize(length * 2L)]; + for (int i = 0; i < length; i++) { + rep[i * 2] = (byte) toHighHexDigit(bytes[fromIndex + i]); + rep[i * 2 + 1] = (byte) toLowHexDigit(bytes[fromIndex + i]); + } + } else if (delimiter.length() == 1 && delimiter.charAt(0) < 256) { + // Allocate the byte array and fill in the characters for the first byte + // Then insert the delimiter and hexadecimal characters for each of the remaining bytes + char sep = delimiter.charAt(0); + rep = new byte[checkMaxArraySize(length * 3L - 1L)]; + rep[0] = (byte) toHighHexDigit(bytes[fromIndex]); + rep[1] = (byte) toLowHexDigit(bytes[fromIndex]); + for (int i = 1; i < length; i++) { + rep[i * 3 - 1] = (byte) sep; + rep[i * 3] = (byte) toHighHexDigit(bytes[fromIndex + i]); + rep[i * 3 + 1] = (byte) toLowHexDigit(bytes[fromIndex + i]); + } + } else { + // Delimiter formatting not to a single byte + return null; + } + try { + // Return a new string using the bytes without making a copy -> we cant use this here as we dont have access to JavaLangAccess + //return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1); + return new String(rep, StandardCharsets.ISO_8859_1); + } catch (Exception cce) { + throw new AssertionError(cce); + } + } + + private char toHighHexDigit(int value) { + return (char) digits[(value >> 4) & 0xf]; + } + + private char toLowHexDigit(int value) { + return (char) digits[value & 0xf]; + } + + private static int checkMaxArraySize(long length) { + if (length > Integer.MAX_VALUE) + throw new OutOfMemoryError("String size " + length + + " exceeds maximum " + Integer.MAX_VALUE); + return (int) length; + } + + @Override + public byte[] decode(CharSequence encoded) { + throw new UnsupportedOperationException(); + } + } } From 9ee7cba3ba8826250c630cf9a0db1b95bd917d40 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sun, 26 Mar 2023 18:56:43 +0200 Subject: [PATCH 34/39] Only run sonar job when on target repo and not on fork PRs or dependabot --- .github/workflows/build_deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index 47ea839..cde13a1 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -41,7 +41,7 @@ jobs: - name: Build with Maven run: ./mvnw -B clean verify -DcommonConfig.jarSign.skip=true - name: Analyze with SonaQube - if: ${{ github.actor != 'dependabot[bot]' }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 37874d515ab2886184ac15fb78b0a80f0051045b Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sun, 26 Mar 2023 18:58:27 +0200 Subject: [PATCH 35/39] Fix checkstyle issue --- src/main/java/at/favre/lib/bytes/MutableBytes.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/at/favre/lib/bytes/MutableBytes.java b/src/main/java/at/favre/lib/bytes/MutableBytes.java index dbc3606..270ae29 100644 --- a/src/main/java/at/favre/lib/bytes/MutableBytes.java +++ b/src/main/java/at/favre/lib/bytes/MutableBytes.java @@ -178,6 +178,11 @@ public Bytes immutable() { return Bytes.wrap(internalArray(), byteOrder()); } + @Override + public int hashCode() { + return Util.Obj.hashCode(internalArray(), byteOrder()); + } + @Override public boolean equals(Object o) { return super.equals(o); From 4cf0997370aa1261b9ad277fc8bf77b43fdbb7a6 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sat, 1 Apr 2023 08:06:21 +0200 Subject: [PATCH 36/39] Update README.md Fix Links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 03720ad..eea6e4b 100644 --- a/README.md +++ b/README.md @@ -623,11 +623,11 @@ readOnlyBytes.inputStream(); ## Download -The artifacts are deployed to [jcenter](https://bintray.com/bintray/jcenter) and [Maven Central](https://search.maven.org/). +The artifacts are deployed to [Maven Central](https://search.maven.org/). ### Maven -Add the dependency of the [latest version](https://github.com/patrickfav/bytes/releases) to your `pom.xml`: +Add the dependency of the [latest version](https://github.com/patrickfav/bytes-java/releases) to your `pom.xml`: ```xml @@ -645,7 +645,7 @@ Add to your `build.gradle` module dependencies: ### Local Jar Library -[Grab jar from latest release.](https://github.com/patrickfav/bytes/releases/latest) +[Grab jar from latest release.](https://github.com/patrickfav/bytes-java/releases/latest) ### OSGi From 4b69d95439a572fbb7c434f527ec5be6cef35f42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 10:56:28 +0000 Subject: [PATCH 37/39] Bump maven-bundle-plugin from 5.1.8 to 5.1.9 Bumps maven-bundle-plugin from 5.1.8 to 5.1.9. --- updated-dependencies: - dependency-name: org.apache.felix:maven-bundle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd65912..b1f97d8 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ org.apache.felix maven-bundle-plugin - 5.1.8 + 5.1.9 true From 03b772a749de11fa08fa51054eca978f4555352c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 10:20:34 +0000 Subject: [PATCH 38/39] Bump org.openjdk.jmh:jmh-generator-annprocess from 1.36 to 1.37 Bumps [org.openjdk.jmh:jmh-generator-annprocess](https://github.com/openjdk/jmh) from 1.36 to 1.37. - [Commits](https://github.com/openjdk/jmh/compare/1.36...1.37) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b1f97d8..b8e042e 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.36 + 1.37 test From 7f47b95aa7dd7e9d2e01dd93567c1b1efc41fca8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 18:13:44 +0000 Subject: [PATCH 39/39] Bump org.openjdk.jmh:jmh-core from 1.36 to 1.37 Bumps [org.openjdk.jmh:jmh-core](https://github.com/openjdk/jmh) from 1.36 to 1.37. - [Commits](https://github.com/openjdk/jmh/compare/1.36...1.37) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b8e042e..2d2e383 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ org.openjdk.jmh jmh-core - 1.36 + 1.37 test