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"
diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml
new file mode 100644
index 0000000..cde13a1
--- /dev/null
+++ b/.github/workflows/build_deploy.yml
@@ -0,0 +1,96 @@
+name: Build and Deploy with Maven
+
+on:
+ push:
+ branches:
+ - main
+ tags:
+ - '*' # Trigger on all tags
+ pull_request: { }
+
+env:
+ SONARQUBE_PROJECT: patrickfav_bytes-java
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ 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 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ - name: Build with Maven
+ run: ./mvnw -B clean verify -DcommonConfig.jarSign.skip=true
+ - name: Analyze with SonaQube
+ 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 }}
+ run: mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=$SONARQUBE_PROJECT
+
+ 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: 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: '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
+ 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: |
+ ./mvnw -B verify nexus-staging:deploy -P deploy -DskipTests && \
+ ./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:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ file: "target/*.jar;target/*.sha256;target/checksum-sha256.txt"
+ tags: true
+ draft: false
diff --git a/.gitignore b/.gitignore
index 3ff6d9e..9536f63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,4 +35,5 @@ Thumbs.db
signing.properties
keystore.jks
ci-settings.xml
-secrets.tar
\ No newline at end of file
+secrets.tar
+pom.xml.versionsBackup
diff --git a/.mvn/maven.config b/.mvn/maven.config
new file mode 100644
index 0000000..f1099e4
--- /dev/null
+++ b/.mvn/maven.config
@@ -0,0 +1 @@
+-DcommonConfig.compiler.profile=jdk7
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
deleted file mode 100644
index b20a55a..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.3";
- /**
- * 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 e89f07c..bf82ff0 100644
Binary files a/.mvn/wrapper/maven-wrapper.jar and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 1703626..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.0/apache-maven-3.6.0-bin.zip
-wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.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 13d8766..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-# To let the CI execute the maven wrapper, use this command and push the change:
-# git update-index --chmod=+x mvnw
-
-sudo: false # route your build to the container-based infrastructure for a faster build
-language: java
-install: true
-jdk:
- - oraclejdk8
-
-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 -X -Djarsigner.skip=true checkstyle:check; fi'
- - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./mvnw clean install -X -Djarsigner.skip=false checkstyle:check; 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/CHANGELOG b/CHANGELOG
index cf96dba..e30ed69 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,51 @@
# 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
+* 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
+* 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)
+* fix `bitAt()` to respect byte order (thx @gfpeltier)
+
+## v1.4.0
+
+* add `from()` constructor from `float[]`
+* add `from()` constructor from `double[]`
+* fix throwing `IllegalArgumentException` instead of `IllegalStateException` in `.toUUID()`
+
+## v1.3.0
+* improve hex encoder performance by factor 5
+
+## v1.2.0
+
+* let hex decoder accept odd length string #37
+
+## v1.1.0
+
+* add `unsecureRandom()` constructor which creates random for e.g. tests or deterministic randoms
+* adds overwrite method to Bytes (thx @JadePaukkunen)
+* make project OSGi compatible #36
+* add `toFloatArray()` converter #30
+* add `toDoubleArray()` converter #30
+
## v1.0.0
* add `append()` method supporting multiple byte arrays #26
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c18fe1f..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 checkstyle:check
+mvn verify
```
Tests are automatically run against branches and pull requests
diff --git a/README.md b/README.md
index 67fe2e1..eea6e4b 100644
--- a/README.md
+++ b/README.md
@@ -27,13 +27,14 @@ to blindly paste code snippets from
[o](https://stackoverflow.com/a/9670279/774398)
[m](https://stackoverflow.com/questions/1519736/random-shuffling-of-an-array)
-[](https://bintray.com/patrickfav/maven/bytes-java/_latestVersion)
-[](https://travis-ci.org/patrickfav/bytes-java)
+[](https://mvnrepository.com/artifact/at.favre.lib/bytes)
+[](https://github.com/patrickfav/bytes-java/actions)
[](https://www.javadoc.io/doc/at.favre.lib/bytes)
-[](https://coveralls.io/github/patrickfav/bytes-java?branch=master)
-[](https://codeclimate.com/github/patrickfav/bytes-java/maintainability)
+[](https://sonarcloud.io/summary/new_code?id=patrickfav_bytes-java)
+[](https://sonarcloud.io/summary/new_code?id=patrickfav_bytes-java)
+[](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), ...
@@ -51,11 +52,13 @@ It is lightweight as it does not require any additional dependencies.
Add dependency to your `pom.xml` ([check latest release](https://github.com/patrickfav/bytes-java/releases)):
-
* 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; @@ -42,7 +42,8 @@ final class BaseEncoding implements BinaryToTextEncoding.EncoderDecoder { private final Alphabet alphabet; private final Character paddingChar; - BaseEncoding(Alphabet alphabet, Character paddingChar) { + @SuppressWarnings("WeakerAccess") + public BaseEncoding(Alphabet alphabet, Character paddingChar) { this.alphabet = Objects.requireNonNull(alphabet); this.paddingChar = paddingChar; } @@ -189,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/BinaryToTextEncoding.java b/src/main/java/at/favre/lib/bytes/BinaryToTextEncoding.java index 00ef667..01f7654 100644 --- a/src/main/java/at/favre/lib/bytes/BinaryToTextEncoding.java +++ b/src/main/java/at/favre/lib/bytes/BinaryToTextEncoding.java @@ -72,6 +72,8 @@ interface EncoderDecoder extends Encoder, Decoder { * Hex or Base16 */ class Hex implements EncoderDecoder { + private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; + private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}; private final boolean upperCase; public Hex() { @@ -84,47 +86,55 @@ public Hex(boolean upperCase) { @Override public String encode(byte[] byteArray, ByteOrder byteOrder) { - StringBuilder sb = new StringBuilder(byteArray.length * 2); + + final char[] buffer = new char[byteArray.length * 2]; + final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER; int index; - char first4Bit; - char last4Bit; for (int i = 0; i < byteArray.length; i++) { index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1; - first4Bit = Character.forDigit((byteArray[index] >> 4) & 0xF, 16); - last4Bit = Character.forDigit((byteArray[index] & 0xF), 16); - if (upperCase) { - first4Bit = Character.toUpperCase(first4Bit); - last4Bit = Character.toUpperCase(last4Bit); - } - sb.append(first4Bit).append(last4Bit); + + buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF]; + buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)]; } - return sb.toString(); + return new String(buffer); } @Override public byte[] decode(CharSequence hexString) { - if (Objects.requireNonNull(hexString, "hex input must not be null").length() % 2 != 0) - throw new IllegalArgumentException("invalid hex string, must be mod 2 == 0"); int start; - if (hexString.length() > 2 && + if (Objects.requireNonNull(hexString).length() > 2 && hexString.charAt(0) == '0' && hexString.charAt(1) == 'x') { start = 2; } else { start = 0; } + int len = hexString.length(); + boolean isOddLength = len % 2 != 0; + if (isOddLength) { + start--; + } + byte[] data = new byte[(len - start) / 2]; int first4Bits; int second4Bits; for (int i = start; i < len; i += 2) { - first4Bits = Character.digit(hexString.charAt(i), 16); + if (i == start && isOddLength) { + first4Bits = 0; + } else { + first4Bits = Character.digit(hexString.charAt(i), 16); + } second4Bits = Character.digit(hexString.charAt(i + 1), 16); if (first4Bits == -1 || second4Bits == -1) { - throw new IllegalArgumentException("'" + hexString.charAt(i) + hexString.charAt(i + 1) + "' at index " + i + " is not hex formatted"); + if (i == start && isOddLength) { + throw new IllegalArgumentException("'" + hexString.charAt(i + 1) + "' at index " + (i + 1) + " is not hex formatted"); + } else { + throw new IllegalArgumentException("'" + hexString.charAt(i) + hexString.charAt(i + 1) + "' at index " + i + " is not hex formatted"); + } } data[(i - start) / 2] = (byte) ((first4Bits << 4) + second4Bits); diff --git a/src/main/java/at/favre/lib/bytes/Bytes.java b/src/main/java/at/favre/lib/bytes/Bytes.java index c834e46..123eeec 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: *
* 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
*/
@@ -108,7 +108,7 @@ public static Bytes empty() {
* @return new instance
*/
public static Bytes wrap(Bytes bytes) {
- return wrap(bytes.internalArray(), bytes.byteOrder);
+ return wrap(Objects.requireNonNull(bytes, "passed Byte instance must not be null").internalArray(), bytes.byteOrder);
}
/**
@@ -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.
*
@@ -346,6 +356,16 @@ public static Bytes from(float float4byte) {
return wrap(ByteBuffer.allocate(4).putFloat(float4byte).array());
}
+ /**
+ * Creates a new instance from given float array.
+ *
+ * @param floatArray to create from
+ * @return new instance
+ */
+ public static Bytes from(float... floatArray) {
+ return wrap(Util.Converter.toByteArray(Objects.requireNonNull(floatArray, "must provide at least a single float")));
+ }
+
/**
* Creates a new instance from given 8 byte floating point number (double).
*
@@ -356,6 +376,16 @@ public static Bytes from(double double8Byte) {
return wrap(ByteBuffer.allocate(8).putDouble(double8Byte).array());
}
+ /**
+ * Creates a new instance from given double array.
+ *
+ * @param doubleArray to create from
+ * @return new instance
+ */
+ public static Bytes from(double... doubleArray) {
+ return wrap(Util.Converter.toByteArray(Objects.requireNonNull(doubleArray, "must provide at least a single double")));
+ }
+
/**
* Creates a new instance from given {@link ByteBuffer}.
* Will use the same backing byte array and honour the buffer's byte order.
@@ -421,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
@@ -538,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
@@ -581,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
@@ -593,11 +623,17 @@ public static Bytes parseRadix(CharSequence radixNumberString, int radix) {
}
/**
- * Parsing of base16/HEX encoded byte arrays. Will accept upper- and lowercase variant and ignores
- * possible "0x" prefix.
+ * Parsing of base16/HEX encoded byte arrays. This is by design a very flexible decoder accepting the following cases:
+ *
+ *
a-f (also mixed case)0x which will be ignored- * 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 @@ -662,6 +698,35 @@ public static Bytes random(int length) { return random(length, new SecureRandom()); } + /** + * A new instance with pseudo random bytes using an unsecure random number generator. + * This may be used in e.g. tests. In production code use {@link #random(int)} per default. + *
+ * ONLY USE IN NON-SECURITY RELEVANT CONTEXT! + * + * @param length desired array length + * @return random instance + */ + public static Bytes unsecureRandom(int length) { + return random(length, new Random()); + } + + /** + * A new instance with pseudo random bytes using an unsecure random number generator. + * This may be used in e.g. tests to create predictable numbers. + *
+ * In production code use {@link #random(int)} per default. + *
+ * ONLY USE IN NON-SECURITY RELEVANT CONTEXT! + * + * @param length desired array length + * @param seed used to seed random number generator - using same seed will generate same numbers + * @return random instance + */ + public static Bytes unsecureRandom(int length, long seed) { + return random(length, new Random(seed)); + } + /** * A new instance with random bytes. * @@ -680,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()); @@ -701,7 +765,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. @@ -714,7 +778,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 @@ -724,7 +788,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 @@ -734,7 +798,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 @@ -744,7 +808,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 @@ -754,7 +818,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 @@ -764,7 +828,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. * @@ -776,7 +840,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 @@ -786,7 +850,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. * @@ -912,7 +976,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 + )); } /** @@ -927,11 +995,15 @@ 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 + )); } /** - * 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 @@ -990,7 +1062,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 @@ -1026,7 +1098,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 @@ -1041,7 +1113,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 @@ -1078,9 +1150,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. * @@ -1126,7 +1198,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; @@ -1154,7 +1226,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; @@ -1204,6 +1276,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. @@ -1238,6 +1324,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
@@ -1278,12 +1383,16 @@ 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) {
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;
+ }
}
/**
@@ -1324,7 +1433,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();
}
/**
@@ -1337,7 +1446,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();
}
/**
@@ -1350,7 +1459,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();
}
/**
@@ -1363,7 +1472,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();
}
/**
@@ -1425,7 +1534,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 @@ -1496,7 +1605,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 @@ -1552,12 +1661,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)}. @@ -1758,7 +1867,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 @@ -1777,11 +1886,10 @@ public BigInteger toBigInteger() { * to a {@link UUID} constructor. * * @return newly created UUID + * @throws IllegalArgumentException if byte array has length not equal to 16 */ public UUID toUUID() { - if (length() != 16) { - throw new IllegalStateException("creating UUID requires internal array to be exactly 16 bytes, was " + length()); - } + Util.Validation.checkExactLength(length(), 16, "UUID"); ByteBuffer byteBuffer = buffer(); return new UUID(byteBuffer.getLong(), byteBuffer.getLong()); } @@ -1793,7 +1901,7 @@ public UUID toUUID() { * If you just want to get the first element as {@code byte}, see {@link #byteAt(int)}, using index zero. * * @return the byte representation - * @throws IllegalStateException if byte array has length not equal to 1 + * @throws IllegalArgumentException if byte array has length not equal to 1 * @see Primitive Types */ public byte toByte() { @@ -1808,7 +1916,7 @@ public byte toByte() { * If you just want to get the first element as {@code byte}, see {@link #byteAt(int)}, using index zero. * * @return the unsigned byte representation wrapped in an int - * @throws IllegalStateException if byte array has length not equal to 1 + * @throws IllegalArgumentException if byte array has length not equal to 1 * @see Primitive Types */ public int toUnsignedByte() { @@ -1823,7 +1931,7 @@ public int toUnsignedByte() { * If you just want to get the first 2 bytes as {@code char}, see {@link #charAt(int)} using index zero. * * @return the int representation - * @throws IllegalStateException if byte array has length not equal to 2 + * @throws IllegalArgumentException if byte array has length not equal to 2 * @see Primitive Types */ public char toChar() { @@ -1838,7 +1946,7 @@ public char toChar() { * If you just want to get the first 2 bytes as {@code short}, see {@link #shortAt(int)} using index zero. * * @return the int representation - * @throws IllegalStateException if byte array has length not equal to 2 + * @throws IllegalArgumentException if byte array has length not equal to 2 * @see Primitive Types */ public short toShort() { @@ -1853,7 +1961,7 @@ public short toShort() { * If you just want to get the first 4 bytes as {@code int}, see {@link #intAt(int)} using index zero. * * @return the int representation - * @throws IllegalStateException if byte array has length not equal to 4 + * @throws IllegalArgumentException if byte array has length not equal to 4 * @see Primitive Types */ public int toInt() { @@ -1887,7 +1995,7 @@ public int[] toIntArray() { * If you just want to get the first 4 bytes as {@code long}, see {@link #longAt(int)} using index zero. * * @return the long representation - * @throws IllegalStateException if byte array has length not equal to 8 + * @throws IllegalArgumentException if byte array has length not equal to 8 * @see Primitive Types */ public long toLong() { @@ -1896,7 +2004,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: *
@@ -1919,7 +2027,7 @@ public long[] toLongArray() {
* representation for a Java float value. The output is dependent on the set {@link #byteOrder()}.
*
* @return the float representation
- * @throws IllegalStateException if byte array has length not equal to 4
+ * @throws IllegalArgumentException if byte array has length not equal to 4
* @see Primitive Types
*/
public float toFloat() {
@@ -1927,12 +2035,31 @@ public float toFloat() {
return internalBuffer().getFloat();
}
+ /**
+ * 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:
+ *
+ * [b1, b2, b3, b4] = [float1]
+ *
+ *
+ * This conversion respects the internal byte order. Will only work if all bytes can be directly mapped to float,
+ * which means the byte array length must be dividable by 4 without rest.
+ *
+ * @return new float[] instance representing this byte array
+ * @throws IllegalArgumentException if internal byte length mod 4 != 0
+ */
+ public float[] toFloatArray() {
+ Util.Validation.checkModLength(length(), 4, "creating an float array");
+ return Util.Converter.toFloatArray(internalArray(), byteOrder);
+ }
+
/**
* If the underlying byte array is exactly 8 byte / 64 bit long, return the
* representation for a Java double value. The output is dependent on the set {@link #byteOrder()}.
*
* @return the double representation
- * @throws IllegalStateException if byte array has length not equal to 8
+ * @throws IllegalArgumentException if byte array has length not equal to 8
* @see Primitive Types
*/
public double toDouble() {
@@ -1940,6 +2067,44 @@ public double toDouble() {
return internalBuffer().getDouble();
}
+ /**
+ * 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:
+ *
+ * [b1, b2, b3, b4, b5, b6, b7, b8] = [double1]
+ *
+ *
+ * This conversion respects the internal byte order. Will only work if all bytes can be directly mapped to double,
+ * which means the byte array length must be dividable by 8 without rest.
+ *
+ * @return new double[] instance representing this byte array
+ * @throws IllegalArgumentException if internal byte length mod 8 != 0
+ */
+ public double[] toDoubleArray() {
+ Util.Validation.checkModLength(length(), 8, "creating an double array");
+ 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}.
@@ -1970,7 +2135,7 @@ public char[] toCharArray(Charset charset) {
* Pairs of {@code byte} elements are compared as if by invoking
* {@link Byte#compare(byte, byte)}.
*
- * Uses {@link ByteBuffer#compareTo(Object)} internally.
+ * Uses {@link ByteBuffer#compareTo(ByteBuffer)} internally.
*
* @return A negative integer, zero, or a positive integer as this buffer
* is less than, equal to, or greater than the given buffer
@@ -2012,7 +2177,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
@@ -2055,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/BytesTransformer.java b/src/main/java/at/favre/lib/bytes/BytesTransformer.java
index 3612505..efdd12c 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);
}
}
@@ -228,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 {
@@ -262,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 f2ae0c7..c1914c0 100644
--- a/src/main/java/at/favre/lib/bytes/BytesTransformers.java
+++ b/src/main/java/at/favre/lib/bytes/BytesTransformers.java
@@ -4,7 +4,6 @@
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Comparator;
@@ -45,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
@@ -55,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.
*
@@ -69,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.
*
@@ -193,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);
@@ -303,65 +302,32 @@ public byte[] transform(byte[] currentArray, boolean inPlace) {
}
private byte[] decompress(byte[] compressedContent) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- GZIPInputStream gzipInputStream = null;
- byte[] returnBuffer;
- try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(Math.max(32, compressedContent.length / 2));
+
+ try (GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedContent))) {
int len;
byte[] buffer = new byte[4 * 1024];
- gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedContent));
while ((len = gzipInputStream.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
- gzipInputStream.close();
- returnBuffer = bos.toByteArray();
- bos.close();
- return returnBuffer;
+ return bos.toByteArray();
} catch (Exception e) {
throw new IllegalStateException("could not decompress gzip", e);
- } finally {
- try {
- bos.close();
- } catch (IOException ignore) {
- }
-
- if (gzipInputStream != null) {
- try {
- gzipInputStream.close();
- } catch (IOException ignore) {
- }
- }
}
}
private byte[] compress(byte[] content) {
ByteArrayOutputStream bos = new ByteArrayOutputStream(content.length);
- GZIPOutputStream gzipOutputStream = null;
- byte[] returnBuffer;
- try {
- gzipOutputStream = new GZIPOutputStream(bos);
+
+ try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bos)) {
gzipOutputStream.write(content);
- gzipOutputStream.close();
- returnBuffer = bos.toByteArray();
- bos.close();
- return returnBuffer;
} catch (Exception e) {
throw new IllegalStateException("could not compress gzip", e);
- } finally {
- try {
- bos.close();
- } catch (IOException ignore) {
- }
-
- if (gzipOutputStream != null) {
- try {
- gzipOutputStream.close();
- } catch (IOException ignore) {
- }
- }
}
+
+ return bos.toByteArray();
}
@Override
diff --git a/src/main/java/at/favre/lib/bytes/BytesValidators.java b/src/main/java/at/favre/lib/bytes/BytesValidators.java
index 067822c..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,18 +107,17 @@ 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);
}
-
/**
* 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);
@@ -128,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 8ad8e91..270ae29 100644
--- a/src/main/java/at/favre/lib/bytes/MutableBytes.java
+++ b/src/main/java/at/favre/lib/bytes/MutableBytes.java
@@ -69,19 +69,30 @@ 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);
}
+ /**
+ * Uses given Bytes array to overwrite internal array
+ *
+ * @param newBytes used to overwrite internal
+ * @return this instance
+ * @throws IndexOutOfBoundsException if newArray.length() > internal length
+ */
+ public MutableBytes overwrite(Bytes newBytes) {
+ return overwrite(newBytes, 0);
+ }
+
/**
* Uses given array to overwrite internal array.
*
* @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");
@@ -89,6 +100,18 @@ public MutableBytes overwrite(byte[] newArray, int offsetInternalArray) {
return this;
}
+ /**
+ * Uses given Bytes array to overwrite internal array.
+ *
+ * @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
+ */
+ public MutableBytes overwrite(Bytes newBytes, int offsetInternalArray) {
+ return overwrite(Objects.requireNonNull(newBytes, "must provide non-null array as source").array(), offsetInternalArray);
+ }
+
/**
* Sets new byte to given index
*
diff --git a/src/main/java/at/favre/lib/bytes/Util.java b/src/main/java/at/favre/lib/bytes/Util.java
index 70d35e0..ac234bb 100644
--- a/src/main/java/at/favre/lib/bytes/Util.java
+++ b/src/main/java/at/favre/lib/bytes/Util.java
@@ -72,7 +72,6 @@ static byte[] concat(byte[]... arrays) {
return result;
}
-
/**
* Combines a single argument with a vararg to a single array
*
@@ -164,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
@@ -190,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
@@ -227,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
@@ -292,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 & 0xff;
+ }
+ 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 & 0xff;
+ }
+ byteArray[i] = dst;
}
- byteArray[i] = dst;
}
}
return byteArray;
@@ -331,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 & 0xff;
+ }
+ 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 & 0xff;
+ }
+ byteArray[i] = dst;
}
- byteArray[i] = dst;
}
}
return byteArray;
@@ -385,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
@@ -463,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.
*
*
@@ -511,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.
*
*
@@ -534,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.
@@ -561,6 +621,33 @@ static byte[] toByteArray(int[] intArray) {
return primitivesArray;
}
+ /**
+ * Creates a byte array from given float array.
+ * The resulting byte array will have length floatArray * 4.
+ *
+ *
+ * Analysis
+ *
+ * - Time Complexity:
O(n)
+ * - Space Complexity:
O(n)
+ * - Alters Parameters:
false
+ *
+ *
+ *
+ * @param floatArray to convert
+ * @return resulting byte array
+ */
+ static byte[] toByteArray(float[] floatArray) {
+ byte[] primitivesArray = new byte[floatArray.length * 4];
+ ByteBuffer buffer = ByteBuffer.allocate(4);
+ for (int i = 0; i < floatArray.length; i++) {
+ buffer.clear();
+ byte[] floatBytes = buffer.putFloat(floatArray[i]).array();
+ System.arraycopy(floatBytes, 0, primitivesArray, (i * 4), floatBytes.length);
+ }
+ return primitivesArray;
+ }
+
/**
* Creates a byte array from given long array.
* The resulting byte array will have length longArray * 8
@@ -588,6 +675,33 @@ static byte[] toByteArray(long[] longArray) {
return primitivesArray;
}
+ /**
+ * Creates a byte array from given double array.
+ * The resulting byte array will have length doubleArray * 8.
+ *
+ *
+ * Analysis
+ *
+ * - Time Complexity:
O(n)
+ * - Space Complexity:
O(n)
+ * - Alters Parameters:
false
+ *
+ *
+ *
+ * @param doubleArray to convert
+ * @return resulting byte array
+ */
+ static byte[] toByteArray(double[] doubleArray) {
+ byte[] primitivesArray = new byte[doubleArray.length * 8];
+ ByteBuffer buffer = ByteBuffer.allocate(8);
+ for (int i = 0; i < doubleArray.length; i++) {
+ buffer.clear();
+ byte[] doubleBytes = buffer.putDouble(doubleArray[i]).array();
+ System.arraycopy(doubleBytes, 0, primitivesArray, (i * 8), doubleBytes.length);
+ }
+ return primitivesArray;
+ }
+
/**
* Converts a char array to a byte array with given charset and range
*
@@ -603,7 +717,7 @@ static byte[] toByteArray(long[] longArray) {
* @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) {
@@ -693,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]
@@ -719,9 +833,90 @@ static long[] toLongArray(byte[] bytes, ByteOrder byteOrder) {
return array;
}
+ /**
+ * Converts the byte array to a float array. This will spread 4 bytes into a single float:
+ *
+ *
+ * [b1, b2, b3, b4] = [float1]
+ *
+ *
+ *
+ * Analysis
+ *
O(n)O(n)false+ * [b1, b2, b3, b4, b5, b6, b7, b8] = [double1] + *+ * + *
+ * Analysis + *
O(n)O(n)false+ * [b1, b2] = [short1] + *+ * + *
+ * Analysis + *
O(n)O(n)false
* Analysis
@@ -839,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
*/
@@ -861,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
*/
@@ -878,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) {
@@ -891,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()) {
@@ -986,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);
diff --git a/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java b/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java
index d08d46f..5cf5058 100644
--- a/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java
+++ b/src/test/java/at/favre/lib/bytes/BytesConstructorTests.java
@@ -34,6 +34,7 @@
import java.nio.IntBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
import java.text.Normalizer;
import java.util.Arrays;
import java.util.BitSet;
@@ -66,6 +67,11 @@ public void wrapTest() {
assertEquals(0, bNullSafe1.length());
}
+ @Test(expected = NullPointerException.class)
+ public void wrapByteNull_shouldThrow() {
+ Bytes.wrap((Bytes) null);
+ }
+
@Test(expected = NullPointerException.class)
public void wrapTestNullExpected() {
Bytes.wrap((byte[]) null);
@@ -73,7 +79,7 @@ public void wrapTestNullExpected() {
@Test
public void wrapTestNullSafe() {
- Bytes.wrapNullSafe(null);
+ assertSame(Bytes.empty(), Bytes.wrapNullSafe(null));
}
@Test
@@ -173,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;
@@ -182,6 +201,11 @@ public void fromInt() {
assertEquals(test, Bytes.from(test).toInt());
}
+ @Test(expected = NullPointerException.class)
+ public void fromIntArray_empty_shouldThrow() {
+ Bytes.from((int[]) null);
+ }
+
@Test
public void fromIntArray() {
assertArrayEquals(new byte[]{0, 0, 0, 1, 0, 0, 0, 2}, Bytes.from(1, 2).array());
@@ -206,6 +230,11 @@ public void fromLong() {
assertEquals(test, Bytes.from(test).toLong());
}
+ @Test(expected = NullPointerException.class)
+ public void fromLongArray_empty_shouldThrow() {
+ Bytes.from((long[]) null);
+ }
+
@Test
public void fromLongArray() {
assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2}, Bytes.from(new long[]{1, 2}).array());
@@ -223,6 +252,22 @@ public void fromFloat() {
assertEquals(test, Bytes.from(test).toFloat(), 0.01);
}
+ @Test(expected = NullPointerException.class)
+ public void fromFloatArray_empty_shouldThrow() {
+ Bytes.from((float[]) null);
+ }
+
+ @Test
+ public void fromFloatArray() {
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 0}, Bytes.from(0f, 0f).array());
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 63, -128, 0, 0}, Bytes.from(0f, 1f).array());
+ assertArrayEquals(new byte[]{0, 0, 0, 0, -65, -128, 0, 0}, Bytes.from(0f, -1f).array());
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Bytes.from(0f, 0f, 0f).array());
+ assertArrayEquals(new byte[]{66, -105, 0, 0, 71, 119, 46, 31}, Bytes.from(75.5f, 63278.123f).array());
+ assertArrayEquals(Bytes.from(Bytes.from(78239.934978f), Bytes.from(-82736.65178f), Bytes.from(0.12879316287461f)).array(),
+ Bytes.from(78239.934978f, -82736.65178f, 0.12879316287461f).array());
+ }
+
@Test
public void fromDouble() {
double test = 3423423.8923423974123;
@@ -232,6 +277,21 @@ public void fromDouble() {
assertEquals(test, Bytes.from(test).toDouble(), 0.01);
}
+ @Test(expected = NullPointerException.class)
+ public void fromDoubleArray_empty_shouldThrow() {
+ Bytes.from((double[]) null);
+ }
+
+ @Test
+ public void fromDoubleArray() {
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Bytes.from(0.0, 0.0).array());
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 63, -16, 0, 0, 0, 0, 0, 0}, Bytes.from(0.0, 1.0).array());
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, -65, -16, 0, 0, 0, 0, 0, 0}, Bytes.from(0.0, -1.0).array());
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Bytes.from(0, 0, 0).array());
+ assertArrayEquals(Bytes.from(Bytes.from(78239.934978), Bytes.from(-82736.65178), Bytes.from(0.12879316287461)).array(),
+ Bytes.from(78239.934978, -82736.65178, 0.12879316287461).array());
+ }
+
@Test
public void fromByteBuffer() {
checkByteBuffer(example_bytes_empty);
@@ -324,6 +384,11 @@ public void toCharArrayShouldThroughNullPointer() {
Bytes.allocate(4).toCharArray(null);
}
+ @Test(expected = NullPointerException.class)
+ public void fromMultipleBytes_empty_shouldThrow() {
+ Bytes.from((Bytes[]) null);
+ }
+
@Test
public void fromMultipleBytes() {
assertArrayEquals(new byte[]{0x01, 0x02, 0x03}, Bytes.from(Bytes.from((byte) 0x01), Bytes.from((byte) 0x02), Bytes.from((byte) 0x03)).array());
@@ -445,6 +510,21 @@ public void fromVariousBytes() {
assertArrayEquals(example_bytes_sixteen, Bytes.fromNullSafe(example_bytes_sixteen).array());
}
+ @Test(expected = NullPointerException.class)
+ public void fromArray_empty_shouldThrow() {
+ Bytes.from((byte[]) null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void wrapArrayByteOrder_empty_shouldThrow() {
+ Bytes.wrap(null, ByteOrder.BIG_ENDIAN);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void fromPartByte_empty_shouldThrow() {
+ Bytes.from((byte[]) null, 0, 1);
+ }
+
@Test
public void fromPartByte() {
assertArrayEquals(new byte[]{example_bytes_four[1]}, Bytes.from(example_bytes_four, 1, 1).array());
@@ -547,4 +627,25 @@ private void testUUID(UUID uuid) {
public void fromUUIDNullArgument() {
Bytes.from((UUID) null);
}
+
+ @Test
+ public void createSecureRandom() {
+ assertNotEquals(Bytes.random(16), Bytes.random(16));
+ }
+
+ @Test
+ public void createSecureRandomWithExplicitSecureRandom() {
+ assertNotEquals(Bytes.random(16, new SecureRandom()), Bytes.random(16, new SecureRandom()));
+ }
+
+ @Test
+ public void createUnsecureRandom() {
+ assertNotEquals(Bytes.unsecureRandom(128), Bytes.unsecureRandom(128));
+ }
+
+ @Test
+ public void createUnsecureRandomWithSeed() {
+ assertEquals(Bytes.unsecureRandom(128, 4L), Bytes.unsecureRandom(128, 4L));
+ assertNotEquals(Bytes.unsecureRandom(4, 3L), Bytes.unsecureRandom(4, 4L));
+ }
}
diff --git a/src/test/java/at/favre/lib/bytes/BytesMiscTest.java b/src/test/java/at/favre/lib/bytes/BytesMiscTest.java
index 068a2ec..a57398d 100644
--- a/src/test/java/at/favre/lib/bytes/BytesMiscTest.java
+++ b/src/test/java/at/favre/lib/bytes/BytesMiscTest.java
@@ -52,14 +52,23 @@ private void testToString(Bytes bytes) {
@Test
public void testHashcode() {
- Bytes instance = Bytes.wrap(example_bytes_seven);
+ Bytes instance = Bytes.from(example_bytes_seven);
assertEquals(instance.hashCode(), instance.hashCode());
assertNotEquals(0, instance.hashCode());
- assertEquals(Bytes.wrap(example_bytes_seven).hashCode(), Bytes.from(example_bytes_seven).hashCode());
- assertEquals(Bytes.wrap(example2_bytes_seven).hashCode(), Bytes.from(example2_bytes_seven).hashCode());
- assertNotEquals(Bytes.wrap(example_bytes_seven).hashCode(), Bytes.wrap(example2_bytes_seven).hashCode());
- assertNotEquals(Bytes.wrap(example_bytes_eight).hashCode(), Bytes.wrap(example2_bytes_seven).hashCode());
- assertNotEquals(0, Bytes.wrap(example2_bytes_seven).hashCode());
+ assertEquals(Bytes.from(example_bytes_seven).hashCode(), Bytes.from(example_bytes_seven).hashCode());
+ assertEquals(Bytes.from(example2_bytes_seven).hashCode(), Bytes.from(example2_bytes_seven).hashCode());
+ assertNotEquals(Bytes.from(example_bytes_seven).hashCode(), Bytes.from(example2_bytes_seven).hashCode());
+ assertNotEquals(Bytes.from(example_bytes_eight).hashCode(), Bytes.from(example2_bytes_seven).hashCode());
+ assertNotEquals(0, Bytes.from(example2_bytes_seven).hashCode());
+ }
+
+ @Test
+ public void testHashcode_changing() {
+ MutableBytes instance = Bytes.from(example_bytes_seven).mutable();
+ assertEquals(instance.hashCode(), Bytes.from(example_bytes_seven).hashCode());
+
+ instance.setByteAt(0, (byte) 0x4B);
+ assertNotEquals(instance.hashCode(), Bytes.from(example_bytes_seven).hashCode());
}
@SuppressWarnings("SimplifiableJUnitAssertion")
@@ -179,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}));
@@ -195,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]));
@@ -263,6 +291,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
diff --git a/src/test/java/at/favre/lib/bytes/BytesParseAndEncodingTest.java b/src/test/java/at/favre/lib/bytes/BytesParseAndEncodingTest.java
index 5938c17..4af006f 100644
--- a/src/test/java/at/favre/lib/bytes/BytesParseAndEncodingTest.java
+++ b/src/test/java/at/favre/lib/bytes/BytesParseAndEncodingTest.java
@@ -42,11 +42,37 @@ public void parseHex() {
assertArrayEquals(defaultArray, Bytes.parseHex("A0E1").array());
assertArrayEquals(defaultArray, Bytes.parseHex("a0e1").array());
assertArrayEquals(defaultArray, Bytes.parseHex(Bytes.parseHex("A0E1").encodeHex()).array());
+ assertArrayEquals(defaultArray, Bytes.parseHex(Bytes.parseHex("a0E1").encodeHex()).array());
+ }
+
+ @Test
+ public void parseHexOddNumberStrings() {
+ assertArrayEquals(new byte[]{(byte) 0x00}, Bytes.parseHex("0").array());
+ assertArrayEquals(new byte[]{(byte) 0x0E}, Bytes.parseHex("E").array());
+ assertArrayEquals(new byte[]{(byte) 0x0A, (byte) 0x0E}, Bytes.parseHex("A0E").array());
+ assertArrayEquals(new byte[]{(byte) 0x03, (byte) 0xEA, (byte) 0x0E}, Bytes.parseHex("3EA0E").array());
+ assertArrayEquals(new byte[]{(byte) 0x00, (byte) 0xF3, (byte) 0xEA, (byte) 0x0E}, Bytes.parseHex("0F3EA0E").array());
+ assertArrayEquals(new byte[]{(byte) 0x0A, (byte) 0xD0, (byte) 0xF3, (byte) 0xEA, (byte) 0x0E}, Bytes.parseHex("AD0F3EA0E").array());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseHexIllegalChars() {
+ Bytes.parseHex("AX");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseHexIllegalChars2() {
+ Bytes.parseHex("XX");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseHexIllegalChars3() {
+ Bytes.parseHex("Y");
}
@Test(expected = IllegalArgumentException.class)
- public void parseHexInvalid() {
- Bytes.parseHex("A0E");
+ public void parseHexIllegalChars4() {
+ Bytes.parseHex("F3El0E");
}
@Test
diff --git a/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java b/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java
index 9b7fdea..efa1559 100644
--- a/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java
+++ b/src/test/java/at/favre/lib/bytes/BytesToConvertOtherTypesTest.java
@@ -222,17 +222,17 @@ public void toDouble() {
}
}
- @Test(expected = IllegalStateException.class)
+ @Test(expected = IllegalArgumentException.class)
public void testToUUIDToLong() {
Bytes.random(17).toUUID();
}
- @Test(expected = IllegalStateException.class)
+ @Test(expected = IllegalArgumentException.class)
public void testToUUIDToShort() {
Bytes.random(15).toUUID();
}
- @Test(expected = IllegalStateException.class)
+ @Test(expected = IllegalArgumentException.class)
public void testToUUIDEmpty() {
Bytes.allocate(0).toUUID();
}
@@ -307,7 +307,6 @@ public void testToLongEmptyArray() {
assertArrayEquals(new long[0], Bytes.empty().toLongArray());
}
-
@Test
public void testToLongArrayLittleEndian() {
assertArrayEquals(new long[]{1}, Bytes.wrap(new byte[]{1, 0, 0, 0, 0, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toLongArray());
@@ -322,4 +321,123 @@ public void testToLongArrayLittleEndian() {
1, 1, 1, 0, 0, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toLongArray());
}
+
+ @Test
+ public void testToFloatArray() {
+ assertArrayEquals(new float[]{1.4E-45f}, Bytes.wrap(new byte[]{0, 0, 0, 1}).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{3.6E-43f}, Bytes.wrap(new byte[]{0, 0, 1, 1}).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{9.2196E-41f}, Bytes.wrap(new byte[]{0, 1, 1, 1}).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{2.3694278E-38f}, Bytes.wrap(new byte[]{1, 1, 1, 1}).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{1.897368E-18f}, Bytes.wrap(new byte[]{34, 12, 0, 69}).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{1.22888184E8f}, Bytes.wrap(new byte[]{76, (byte) 234, 99, (byte) 255}).toFloatArray(), 0.01f);
+
+ assertArrayEquals(new float[]{1.4E-45f, 1.4E-45f}, Bytes.wrap(new byte[]{0, 0, 0, 1, 0, 0, 0, 1}).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{3.6E-43f, 1.4E-45f}, Bytes.wrap(new byte[]{0, 0, 1, 1, 0, 0, 0, 1}).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{3.6E-43f, 9.2196E-41f, 1.4E-45f}, Bytes.wrap(new byte[]{0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1}).toFloatArray(), 0.01f);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testToFloatArrayNotMod4Was5Byte() {
+ Bytes.wrap(new byte[]{1, 0, 0, 0, 1}).toFloatArray();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testToFloatArrayNotMod4Only3Byte() {
+ Bytes.wrap(new byte[]{0, 0, 1}).toFloatArray();
+ }
+
+ @Test
+ public void testToFloatEmptyArray() {
+ assertArrayEquals(new float[0], Bytes.empty().toFloatArray(), 0.01f);
+ }
+
+ @Test
+ public void testToFloatArrayLittleEndian() {
+ assertArrayEquals(new float[]{1.4E-45f}, Bytes.wrap(new byte[]{1, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{3.6E-43f}, Bytes.wrap(new byte[]{1, 1, 0, 0}, ByteOrder.LITTLE_ENDIAN).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{1.22888184E8f}, Bytes.wrap(new byte[]{(byte) 255, 99, (byte) 234, 76}, ByteOrder.LITTLE_ENDIAN).toFloatArray(), 0.01f);
+
+ assertArrayEquals(new float[]{1.4E-45f, 1.4E-45f}, Bytes.wrap(new byte[]{1, 0, 0, 0, 1, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{3.6E-43f, 1.4E-45f}, Bytes.wrap(new byte[]{1, 1, 0, 0, 1, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toFloatArray(), 0.01f);
+ assertArrayEquals(new float[]{3.6E-43f, 9.2196E-41f, 1.4E-45f}, Bytes.wrap(new byte[]{1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toFloatArray(), 0.01f);
+ }
+
+ @Test
+ public void testToDoubleArray() {
+ assertArrayEquals(new double[]{1.4E-45}, Bytes.wrap(new byte[]{0, 0, 0, 0, 0, 0, 0, 1}).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{3.6E-43}, Bytes.wrap(new byte[]{0, 0, 0, 0, 0, 0, 1, 1}).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{4.228405109821336E-86}, Bytes.wrap(new byte[]{46, 53, 7, 98, 34, 12, 0, 69}).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{-2.385279556059394E-168}, Bytes.wrap(new byte[]{(byte) 157, 34, 1, 0, 76, (byte) 234, 99, (byte) 255}).toDoubleArray(), 0.01);
+
+ assertArrayEquals(new double[]{1.4E-45, 1.4E-45}, Bytes.wrap(new byte[]{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{3.6E-43, 1.4E-45}, Bytes.wrap(new byte[]{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1}).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{5.43230922614E-312, 1.39067116189206E-309, 1.4E-45}, Bytes.wrap(new byte[]{
+ 0, 0, 1, 0, 0, 0, 1, 1,
+ 0, 1, 0, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 1}).toDoubleArray(), 0.01);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testToDoubleArrayNotMod4Was9Byte() {
+ Bytes.wrap(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 1}).toDoubleArray();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testToDoubleArrayNotMod4Only7Byte() {
+ Bytes.wrap(new byte[]{0, 0, 0, 0, 0, 0, 1}).toDoubleArray();
+ }
+
+ @Test
+ public void testToDoubleEmptyArray() {
+ assertArrayEquals(new double[0], Bytes.empty().toDoubleArray(), 0.01);
+ }
+
+ @Test
+ public void testToDoubleArrayLittleEndian() {
+ assertArrayEquals(new double[]{1.4E-45}, Bytes.wrap(new byte[]{1, 0, 0, 0, 0, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{3.6E-43}, Bytes.wrap(new byte[]{1, 1, 0, 0, 0, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{4.228405109821336E-86}, Bytes.wrap(new byte[]{69, 0, 12, 34, 98, 7, 53, 46}, ByteOrder.LITTLE_ENDIAN).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{-2.385279556059394E-168}, Bytes.wrap(new byte[]{(byte) 255, 99, (byte) 234, 76, 0, 1, 34, (byte) 157}, ByteOrder.LITTLE_ENDIAN).toDoubleArray(), 0.01);
+
+ assertArrayEquals(new double[]{1.4E-45, 1.4E-45}, Bytes.wrap(new byte[]{1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{3.6E-43f, 1.4E-45}, Bytes.wrap(new byte[]{1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, ByteOrder.LITTLE_ENDIAN).toDoubleArray(), 0.01);
+ assertArrayEquals(new double[]{5.43230922614E-312, 1.39067116189206E-309, 1.4E-45}, Bytes.wrap(new byte[]{
+ 1, 1, 0, 0, 0, 1, 0, 0,
+ 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/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/EncodingJmhBenchmark.java b/src/test/java/at/favre/lib/bytes/EncodingBase64JmhBenchmark.java
similarity index 70%
rename from src/test/java/at/favre/lib/bytes/EncodingJmhBenchmark.java
rename to src/test/java/at/favre/lib/bytes/EncodingBase64JmhBenchmark.java
index f302292..c169185 100644
--- a/src/test/java/at/favre/lib/bytes/EncodingJmhBenchmark.java
+++ b/src/test/java/at/favre/lib/bytes/EncodingBase64JmhBenchmark.java
@@ -59,23 +59,19 @@
@Measurement(iterations = 4, time = 5)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
-public class EncodingJmhBenchmark {
+public class EncodingBase64JmhBenchmark {
@Param({"1", "16", "128", "512", "1000000"})
private int byteLength;
private Map
+ * # Run complete. Total time: 00:22:00
+ *
+ * REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
+ * why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
+ * experiments, perform baseline and negative tests that provide experimental control, make sure
+ * the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
+ * Do not assume the numbers tell you what you want them to tell.
+ *
+ * Benchmark (byteLength) Mode Cnt Score Error Units
+ * EncodingHexJmhBenchmark.encodeBigInteger 4 thrpt 3 9427359,108 ± 194693,624 ops/s
+ * EncodingHexJmhBenchmark.encodeBigInteger 8 thrpt 3 2638943,318 ± 15520,030 ops/s
+ * EncodingHexJmhBenchmark.encodeBigInteger 16 thrpt 3 2088514,337 ± 50090,292 ops/s
+ * EncodingHexJmhBenchmark.encodeBigInteger 32 thrpt 3 1002560,730 ± 11096,621 ops/s
+ * EncodingHexJmhBenchmark.encodeBigInteger 128 thrpt 3 133665,420 ± 5750,982 ops/s
+ * EncodingHexJmhBenchmark.encodeBigInteger 512 thrpt 3 25807,764 ± 407,273 ops/s
+ * EncodingHexJmhBenchmark.encodeBigInteger 1000000 thrpt 3 4,178 ± 0,021 ops/s
+ * EncodingHexJmhBenchmark.encodeBouncyCastleHex 4 thrpt 3 14260598,238 ± 4113483,586 ops/s
+ * EncodingHexJmhBenchmark.encodeBouncyCastleHex 8 thrpt 3 10910660,517 ± 51077,989 ops/s
+ * EncodingHexJmhBenchmark.encodeBouncyCastleHex 16 thrpt 3 7501666,934 ± 938571,073 ops/s
+ * EncodingHexJmhBenchmark.encodeBouncyCastleHex 32 thrpt 3 3657718,637 ± 214574,329 ops/s
+ * EncodingHexJmhBenchmark.encodeBouncyCastleHex 128 thrpt 3 1077236,523 ± 78734,473 ops/s
+ * EncodingHexJmhBenchmark.encodeBouncyCastleHex 512 thrpt 3 266238,359 ± 2544,657 ops/s
+ * EncodingHexJmhBenchmark.encodeBouncyCastleHex 1000000 thrpt 3 152,235 ± 8,156 ops/s
+ * EncodingHexJmhBenchmark.encodeBytesLib 4 thrpt 3 24944896,332 ± 2367456,841 ops/s
+ * EncodingHexJmhBenchmark.encodeBytesLib 8 thrpt 3 23222999,376 ± 396218,748 ops/s
+ * EncodingHexJmhBenchmark.encodeBytesLib 16 thrpt 3 20423170,033 ± 194747,311 ops/s
+ * EncodingHexJmhBenchmark.encodeBytesLib 32 thrpt 3 15723370,706 ± 148740,280 ops/s
+ * EncodingHexJmhBenchmark.encodeBytesLib 128 thrpt 3 6685522,295 ± 920540,781 ops/s
+ * EncodingHexJmhBenchmark.encodeBytesLib 512 thrpt 3 1612325,148 ± 18002,919 ops/s
+ * EncodingHexJmhBenchmark.encodeBytesLib 1000000 thrpt 3 825,527 ± 124,972 ops/s
+ * EncodingHexJmhBenchmark.encodeOldBytesLib 4 thrpt 3 17973467,504 ± 429817,214 ops/s
+ * EncodingHexJmhBenchmark.encodeOldBytesLib 8 thrpt 3 14836039,023 ± 16437596,584 ops/s
+ * EncodingHexJmhBenchmark.encodeOldBytesLib 16 thrpt 3 11727630,076 ± 1849815,074 ops/s
+ * EncodingHexJmhBenchmark.encodeOldBytesLib 32 thrpt 3 7440067,345 ± 10968063,752 ops/s
+ * EncodingHexJmhBenchmark.encodeOldBytesLib 128 thrpt 3 2656731,985 ± 53887,109 ops/s
+ * EncodingHexJmhBenchmark.encodeOldBytesLib 512 thrpt 3 288949,898 ± 7650,867 ops/s
+ * EncodingHexJmhBenchmark.encodeOldBytesLib 1000000 thrpt 3 127,737 ± 3,982 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode1 4 thrpt 3 24557504,065 ± 797466,455 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode1 8 thrpt 3 23515327,490 ± 979922,009 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode1 16 thrpt 3 20377272,084 ± 480955,369 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode1 32 thrpt 3 16273674,820 ± 745874,444 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode1 128 thrpt 3 6767893,154 ± 511154,141 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode1 512 thrpt 3 1573543,465 ± 1126382,628 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode1 1000000 thrpt 3 833,885 ± 54,099 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode2 4 thrpt 3 25220997,342 ± 852230,094 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode2 8 thrpt 3 24152600,074 ± 480651,708 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode2 16 thrpt 3 21494551,701 ± 1722250,353 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode2 32 thrpt 3 17283936,474 ± 134694,570 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode2 128 thrpt 3 7554272,476 ± 1329386,082 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode2 512 thrpt 3 1781989,859 ± 125696,811 ops/s
+ * EncodingHexJmhBenchmark.encodeStackOverflowCode2 1000000 thrpt 3 935,685 ± 103,571 ops/s
+ * EncodingHexJmhBenchmark.encodeGuavaBase16 4 thrpt 3 17606767,347 ± 3615884,433 ops/s
+ * EncodingHexJmhBenchmark.encodeGuavaBase16 8 thrpt 3 14077449,253 ± 3570354,199 ops/s
+ * EncodingHexJmhBenchmark.encodeGuavaBase16 16 thrpt 3 10177925,910 ± 5584265,227 ops/s
+ * EncodingHexJmhBenchmark.encodeGuavaBase16 32 thrpt 3 6270140,774 ± 96756,395 ops/s
+ * EncodingHexJmhBenchmark.encodeGuavaBase16 128 thrpt 3 2094658,415 ± 2321008,783 ops/s
+ * EncodingHexJmhBenchmark.encodeGuavaBase16 512 thrpt 3 514528,610 ± 20419,732 ops/s
+ * EncodingHexJmhBenchmark.encodeGuavaBase16 1000000 thrpt 3 257,440 ± 23,100 ops/s
+ * EncodingHexJmhBenchmark.encodeSpringSecurity 4 thrpt 3 25269684,873 ± 1820484,918 ops/s
+ * EncodingHexJmhBenchmark.encodeSpringSecurity 8 thrpt 3 22631565,512 ± 291386,502 ops/s
+ * EncodingHexJmhBenchmark.encodeSpringSecurity 16 thrpt 3 18704986,566 ± 604936,041 ops/s
+ * EncodingHexJmhBenchmark.encodeSpringSecurity 32 thrpt 3 13345680,362 ± 59610,208 ops/s
+ * EncodingHexJmhBenchmark.encodeSpringSecurity 128 thrpt 3 4904805,240 ± 18454,315 ops/s
+ * EncodingHexJmhBenchmark.encodeSpringSecurity 512 thrpt 3 1165535,921 ± 13487,051 ops/s
+ * EncodingHexJmhBenchmark.encodeSpringSecurity 1000000 thrpt 3 601,152 ± 248,099 ops/s
+ * EncodingHexJmhBenchmark.encodeApacheCommons 4 thrpt 3 24942725,562 ± 4311405,167 ops/s
+ * EncodingHexJmhBenchmark.encodeApacheCommons 8 thrpt 3 21908225,962 ± 488932,267 ops/s
+ * EncodingHexJmhBenchmark.encodeApacheCommons 16 thrpt 3 17503857,494 ± 37335,212 ops/s
+ * EncodingHexJmhBenchmark.encodeApacheCommons 32 thrpt 3 12378489,920 ± 541212,742 ops/s
+ * EncodingHexJmhBenchmark.encodeApacheCommons 128 thrpt 3 4319898,906 ± 15591,318 ops/s
+ * EncodingHexJmhBenchmark.encodeApacheCommons 512 thrpt 3 1034983,537 ± 8517,121 ops/s
+ * EncodingHexJmhBenchmark.encodeApacheCommons 1000000 thrpt 3 529,885 ± 223,283 ops/s
+ * EncodingHexJmhBenchmark.encodeJaxBDataTypeConverter 16 thrpt 3 13497736,529 ± 871841,900 ops/s
+ * EncodingHexJmhBenchmark.encodeJaxBDataTypeConverter 32 thrpt 3 8312834,453 ± 196657,729 ops/s
+ * EncodingHexJmhBenchmark.encodeJaxBDataTypeConverter 128 thrpt 3 2590940,123 ± 50999,492 ops/s
+ * EncodingHexJmhBenchmark.encodeJaxBDataTypeConverter 1000000 thrpt 3 346,074 ± 29,339 ops/s
+ */
+
+@State(Scope.Thread)
+@Fork(1)
+@Warmup(iterations = 2, time = 3)
+@Measurement(iterations = 3, time = 10)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+public class EncodingHexJmhBenchmark {
+
+ @Param({"4", "8", "16", "32", "128", "512", "1000000"})
+ private int byteLength;
+ private Map