From b54f29249310a00731b46d519555ac220e82cda6 Mon Sep 17 00:00:00 2001 From: Patrick Favre-Bulle Date: Sun, 26 Mar 2023 18:03:06 +0200 Subject: [PATCH 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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