From 317c4cf2b98fa3274b359baa37ebbe61eee766de Mon Sep 17 00:00:00 2001 From: stroomdev66 Date: Mon, 29 Sep 2025 17:16:28 +0100 Subject: [PATCH 1/7] Use Java 25 --- .sdkmanrc | 2 +- build.gradle | 2 +- container_build/docker_java/Dockerfile | 2 +- gradle/gradle-daemon-jvm.properties | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 9 +-- gradlew.bat | 3 +- stroom-app/docker/Dockerfile | 2 +- .../stroom-proxy-app/docker/Dockerfile | 2 +- unreleased_changes/20250929_155237_704__0.md | 55 ++++++++++++++++++ 11 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 unreleased_changes/20250929_155237_704__0.md diff --git a/.sdkmanrc b/.sdkmanrc index 1a28648b26c..8427edd8456 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1,4 +1,4 @@ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below -java=21.0.8-tem +java=25-tem diff --git a/build.gradle b/build.gradle index a235d2235dc..d7709244c37 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ plugins { apply plugin: 'de.undercouch.download' -ext.javaLanguageVersion = 21 +ext.javaLanguageVersion = 25 // Adoptium also covers Temurin ext.javaVendor = JvmVendorSpec.ADOPTIUM diff --git a/container_build/docker_java/Dockerfile b/container_build/docker_java/Dockerfile index ba24a6be271..c199f6c3c5b 100644 --- a/container_build/docker_java/Dockerfile +++ b/container_build/docker_java/Dockerfile @@ -18,7 +18,7 @@ # It also contains plantuml for plant uml image generation # Using 'openjdk' on Alpine is not fully supported so using Eclipse Temurin JDK to ensure we have a known jdk version # See https://github.com/docker-library/docs/blob/master/openjdk/README.md#openjdkversion-alpine -FROM eclipse-temurin:21.0.7_6-jdk-alpine +FROM eclipse-temurin:25_36-jdk-alpine-3.22 # Work from the shared git repo dir WORKDIR /builder/shared diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index 63e5bbdf484..5a334ba9a3d 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -1,2 +1,2 @@ #This file is generated by updateDaemonJvm -toolchainVersion=21 +toolchainVersion=25 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..8bdaf60c75ab801e22807dde59e12a8735a34077 100644 GIT binary patch delta 37330 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e18bc253b85..2e1113280ef 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b0d4..adff685a034 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -205,15 +203,14 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9b42019c791..e509b2dd8fe 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/stroom-app/docker/Dockerfile b/stroom-app/docker/Dockerfile index d92ec91f0d2..c13cdbfc404 100644 --- a/stroom-app/docker/Dockerfile +++ b/stroom-app/docker/Dockerfile @@ -23,7 +23,7 @@ # jstat/jmap/jcmd/etc. # Using 'openjdk' on Alpine is not fully supported so using Eclipse Temurin JDK to ensure we have a known jdk version # See https://github.com/docker-library/docs/blob/master/openjdk/README.md#openjdkversion-alpine -FROM eclipse-temurin:21.0.8_9-jdk-alpine as stroom-base-stage +FROM eclipse-temurin:25_36-jdk-alpine-3.22 as stroom-base-stage # bash and jq are required for Kubernetes lifecycle scripts, which interact with the API # curl is required for the docker healthcheck diff --git a/stroom-proxy/stroom-proxy-app/docker/Dockerfile b/stroom-proxy/stroom-proxy-app/docker/Dockerfile index d1db8984675..5a3e29cc28a 100644 --- a/stroom-proxy/stroom-proxy-app/docker/Dockerfile +++ b/stroom-proxy/stroom-proxy-app/docker/Dockerfile @@ -23,7 +23,7 @@ # jstat/jmap/jcmd/etc. # Using 'openjdk' on Alpine is not fully supported so using Eclipse Temurin JDK to ensure we have a known jdk version # See https://github.com/docker-library/docs/blob/master/openjdk/README.md#openjdkversion-alpine -FROM eclipse-temurin:21.0.8_9-jdk-alpine as stroom-base-stage +FROM eclipse-temurin:25_36-jdk-alpine-3.22 as stroom-base-stage # curl is required for the docker healthcheck # su-exec required for running stroom as not-root user diff --git a/unreleased_changes/20250929_155237_704__0.md b/unreleased_changes/20250929_155237_704__0.md new file mode 100644 index 00000000000..b53942e84f9 --- /dev/null +++ b/unreleased_changes/20250929_155237_704__0.md @@ -0,0 +1,55 @@ +* Uplift docker image JDK to `eclipse-temurin:25_36-jdk-alpine-3.22`. + + +```sh +# ONLY the top line will be included as a change entry in the CHANGELOG. +# The entry should be in GitHub flavour markdown and should be written on a SINGLE +# line with no hard breaks. You can have multiple change files for a single GitHub issue. +# The entry should be written in the imperative mood, i.e. 'Fix nasty bug' rather than +# 'Fixed nasty bug'. +# +# Examples of acceptable entries are: +# +# +# * Issue **123** : Fix bug with an associated GitHub issue in this repository +# +# * Issue **namespace/other-repo#456** : Fix bug with an associated GitHub issue in another repository +# +# * Fix bug with no associated GitHub issue. + + +# -------------------------------------------------------------------------------- +# The following is random text to make this file unique for git's change detection +# yr776q2zhysVAjaqa7trGdJeu2B1ZhnkO4Pwcht5ORyHEBL5W85b9N7vkh4qIg4NvQIkYxWsuto3FiId +# e8q8SkwmP12NBl2fgKR80xzKywXWSO77SECSd6sjGkiCyJenhi2K0ErE6i9rFhgCNGFchCuf2baQTd59 +# So4FO7Q6bN2rqf7qAcFvn3AWAnatNky3qhG24B08TsCEnFIzkxQir3tsosRWQy2OQJHQfzSvGUQ7lejn +# BSMdnihwjUFfdb4IM3MSvpOANV0sTohrkbPEJi7DEB3Nco59JAjmfvtSTyVp2b3kcjjmRuRlPeDAcbaS +# lcLNoJm4vmvPlc7SrcHWRBaQ7lXDmju3KkxGlh4aYy5psWQOPn1Szpia1jXJaCi7Jth5u1w5ivmh51CL +# YT5culhn07GngjuHPgWZTb22LZ3aKf5CNGVpEHpFhyoVZpAbZ4n3oOvOaLdPLQb92i6NAsyGQ9DncRZZ +# uomCrZ5H4Sb7utLUJRdPZZGdhs4ycvo2YavOBizzRsDcPV9GuwywIUsBNZKowayXK50Kypk0Ng9sOLRk +# Ed08oyLRrmj0c7MS2Q26tUIGbjPT7OjV5s26IHX6wm2F83r0mA4RbQJgmi4KZQNbCIFTPsMxSte7llEH +# BU6BHJyHRN7mI8TfEElkoQbYTcWrA7oTT2J3yTcx1Q0Lf4cCGGVXtaECubHzxmzHCpVaqBziogth4mqT +# bxAspho777Qn7X4wBlOPA8eSGZIIDshajXFje36W4CVPAlyYph0FmdFEOR2EwZA0Uy0lDlywDskg0J4B +# Fzmvqi4Cih0uCPmep4mFPVwlomfpiZtN5Z1eTVDq5qG5l3UN4HWhHYVFoM9xCcyjpbLELF2G4Ie5BHoW +# pFdCtJfSCzoOrzsoIDDFf5P53ihFMb5R5sjwHLRWp5j7NATbOWfgLGeT5gYETn4n2v9atjIx431q0XHL +# mimd4DDZQ8wsg8ci9Tx7bNIfodueBwNLYy4ClFn5ucq8an6rIsCk9208fB7JrWelqj9CUTwkgFWmfkLl +# 3lkkvYwQh9We4iyNgVfWQzwsXs3vgeiddB8ZQcYVIK8EVUf4fOmqSOXLpURaGIBlgRrcWGiksZDoT37s +# PjA4P4g2E8Vu1QRtvAjFHwqMAIQoEK4OU7b1X12P221oOZuyzAdT4SGX8G5TgxVR7S3hhNGbDBEDhIQu +# 1hOCoqV08YWlzHaGeyLFLlk71l709cp9ITUZsMwA4KxGXwxj0c1Pkel3aat9hdFg5lqD4PfdyTUW1HrN +# h1zLgeC1XtegO1EmA71DBkc3O2il4r381GEKMAJRCAmKDskKS8tlxIaDYiEp7PsO7MQ9q5QW3vwVH3d0 +# WwHYswoIoqb6LESZsMmE6vMcKQrTY3maJahBJ7meAuFnjUcsVfooKR93jYDu8G4Kvijhews1WQxLU9uF +# 6IEDieeS93HjqPyYdVceFmrZXlk83jeAU7ggkjF0y9Ko8HYfqiinbjHyN4QAdmXoVtHSOOGwrK5BPNNV +# KBwZCTZFIOySTH3xwe4qvLDL2x5feebWGwQtpCZt13DS3JHFlD3ItNwOcgmEMykTzRgVCjh3pAD0Yxuh +# ldJucKYT54DBOzqpBIsHkCwo5ZE2Rr5Nqu7VQdXqQRMjLK35jEPJtdCibTd3Q4kwPWYkLONpKXra5reL +# fvvE7gvlaWc5h18yoJtjNXmXaWro2fDfJVnWXqU5qBaRIomh99A8GlX69Mo3hfRO0BLivX6fIE5SV0Rr +# BODUaOxCTKNiLJHzMYrF0SNK2U0avVQxdIkvWXLOlKSBqPcakHM3xk6ek0aiKhPPCcZfZwyomUtA2hzy +# CA0NT9VXTUu0dzBeEQvyENdu77OSVqfYUiSLasGp3Rp3jcBICq9dYgbYvBE6ZFKqLUkRlWLd4m0DGrLY +# knZFXy73WoA6zu9k1C9nySsYmqvK73FLlUNFskIqapjpnpoAehtLnqQ4KW1yAJwdJCyZNLHqhU35xMCc +# AluVt9moa3smPSO4gFMkk6E6DwtuXBOfaJFiXtsx7La3Uw3DnTcmKIxwMvjZqnEmbWo5K2UdUTt6YqVF +# M3exlMKz63FVkpzbNt1myCaTZtVKMhdolyJr0nMn5ScKr7SzHwmKXYkBRwvZKDptxExCLDBrhMz1ZOX0 +# WpMFMXKOsXYmOugVizE4G3GYr7OF68HL2TlHiR3z7A4VwZ4miV9p89kiw9mlUUXkqpwrDoP3xWNPuejj +# kXlKGmLulKZq3bxWvbfoe0ZL8RhZuAdvtPW2WODI974980Y2AFAGK18F7Cvcku6kzz9kFHzmALN0WX1E +# Bze5UASkMbe4rxghVcyYQwjstRqn8h7HhQGyDRQMi2uLW1AQ8HxigKJGxZE8sRSjGMB60k40T1aeh8aO +# -------------------------------------------------------------------------------- + +``` From e06e4b5c26a3a5f9903a85e3dac34af055578f7a Mon Sep 17 00:00:00 2001 From: stroomdev66 Date: Tue, 30 Sep 2025 12:38:51 +0100 Subject: [PATCH 2/7] Remove uses of unsafe --- gradle/libs.versions.toml | 2 +- stroom-aws/stroom-aws-s3-impl/build.gradle | 4 + stroom-bytebuffer/build.gradle | 1 - .../stroom/bytebuffer/ByteBufferUtils.java | 24 - .../PooledByteBufferOutputStream.java | 6 + .../bytebuffer/hbase/ByteBufferUtils.java | 1463 --------- .../java/stroom/bytebuffer/hbase/Bytes.java | 2743 ----------------- .../hbase/HBasePlatformDependent.java | 577 ---- .../bytebuffer/hbase/HBaseSignalInternal.java | 40 - .../bytebuffer/hbase/HBaseUnsafeInternal.java | 420 --- .../stroom/bytebuffer/hbase/UnsafeAccess.java | 478 --- .../bytebuffer/TestByteBufferUtils.java | 7 +- .../TestPooledByteBufferOutputStream.java | 5 +- .../offheapstore/databases/ValueStoreDb.java | 2 +- .../serdes/StagingRefDataValueSerde.java | 3 +- .../serdes/ValueStoreKeySerde.java | 15 +- .../serdes/ValueStoreMetaSerde.java | 28 +- stroom-state/stroom-state-impl/build.gradle | 4 + .../impl/hbase/rollup/RollUpBitMask.java | 76 +- .../impl/sql/rollup/ByteArrayUtils.java | 76 - .../impl/sql/rollup/RollUpBitMask.java | 48 +- .../impl/sql/rollup/TestRollUpBitMask.java | 1 + 22 files changed, 71 insertions(+), 5952 deletions(-) delete mode 100644 stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/ByteBufferUtils.java delete mode 100644 stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/Bytes.java delete mode 100644 stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBasePlatformDependent.java delete mode 100644 stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseSignalInternal.java delete mode 100644 stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseUnsafeInternal.java delete mode 100644 stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/UnsafeAccess.java delete mode 100644 stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/ByteArrayUtils.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 76d6b80b6b9..7c11765dfaf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,7 +48,7 @@ commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" } commons-lang = { module = "org.apache.commons:commons-lang3" } # version controlled by dropwizard-dependencies commons-pool2 = { module = "org.apache.commons:commons-pool2", version = "2.12.0" } commons-text = { module = "org.apache.commons:commons-text" } # version controlled by dropwizard-dependencies -classgraph = { module = "io.github.classgraph:classgraph", version = "4.8.179" } +classgraph = { module = "io.github.classgraph:classgraph", version = "4.8.181" } data-faker = { module = "net.datafaker:datafaker", version = "2.4.2" } dropwizard-assets = { module = "io.dropwizard:dropwizard-assets" } # version controlled by dropwizard-dependencies dropwizard-auth = { module = "io.dropwizard:dropwizard-auth" } # version controlled by dropwizard-dependencies diff --git a/stroom-aws/stroom-aws-s3-impl/build.gradle b/stroom-aws/stroom-aws-s3-impl/build.gradle index 35502d61ae6..45e3fc22bc2 100644 --- a/stroom-aws/stroom-aws-s3-impl/build.gradle +++ b/stroom-aws/stroom-aws-s3-impl/build.gradle @@ -33,3 +33,7 @@ dependencies { testImplementation libs.bundles.common.test.implementation testRuntimeOnly libs.bundles.common.test.runtime } + +tasks.withType(AbstractTestTask).configureEach { + failOnNoDiscoveredTests = false +} diff --git a/stroom-bytebuffer/build.gradle b/stroom-bytebuffer/build.gradle index 30d81bc2814..8ba99169f30 100644 --- a/stroom-bytebuffer/build.gradle +++ b/stroom-bytebuffer/build.gradle @@ -1,7 +1,6 @@ ext.moduleName = 'stroom.bytebuffer' dependencies { - implementation project(':stroom-hadoop') implementation project(':stroom-util') implementation project(':stroom-util-shared') // diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java index a4124bfa0ad..5e8beb1abbc 100644 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java +++ b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java @@ -200,30 +200,6 @@ public static String byteBufferInfo(final ByteBuffer byteBuffer) { StandardCharsets.UTF_8.decode(byteBuffer.duplicate())); } -// public static String byteBufferToAllForms(final ByteBuffer byteBuffer) { -// if (byteBuffer == null) { -// return "null"; -// } -// return ByteArrayUtils.byteArrayToAllForms(toBytes(byteBuffer)); -// } -// -// public static int compare(final ByteBuffer left, final ByteBuffer right) { -// int cmpResult = stroom.bytebuffer.hbase.ByteBufferUtils.compareTo( -// left, left.position(), left.remaining(), -// right, right.position(), right.remaining()); -// -// LOGGER.trace(() -> LogUtil.message("compare({}, {}) returned {}", -// ByteBufferUtils.byteBufferInfo(left), -// ByteBufferUtils.byteBufferInfo(right), -// cmpResult)); -// return cmpResult; -// -// } - - public static int compareTo(final ByteBuffer buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - return stroom.bytebuffer.hbase.ByteBufferUtils.compareTo(buf1, o1, l1, buf2, o2, l2); - } - /** * Compare two {@link ByteBuffer} objects as if they are longs * diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/PooledByteBufferOutputStream.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/PooledByteBufferOutputStream.java index 7de4b54e043..680d3b23f1c 100644 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/PooledByteBufferOutputStream.java +++ b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/PooledByteBufferOutputStream.java @@ -125,6 +125,12 @@ public void write(final byte[] b, final int off, final int len) throws IOExcepti } } + public void writeLong(final long l) throws IOException { + checkWriteableState(); + checkSizeAndGrow(Long.BYTES); + getCurrentPooledBuffer().getByteBuffer().putLong(l); + } + /** * Writes byteBuffer into the outputStream. Respects the position/limit of byteBuffer. * After reading, byteBuffer is rewound to return it to its passed state. diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/ByteBufferUtils.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/ByteBufferUtils.java deleted file mode 100644 index 72688a312df..00000000000 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/ByteBufferUtils.java +++ /dev/null @@ -1,1463 +0,0 @@ -/* - * Copy of https://github.com/apache/hbase/blob/master/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java - * to avoid having to pull in all of hbase to use the util methods. - * - * 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 - * - * 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. - */ - -package stroom.bytebuffer.hbase; - -import org.apache.hadoop.io.IOUtils; -import org.apache.hadoop.io.WritableUtils; - -import java.io.ByteArrayOutputStream; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.Arrays; - -/** - * Utility functions for working with byte buffers, such as reading/writing variable-length long - * numbers. - */ -public final class ByteBufferUtils { - - // "Compressed integer" serialization helper constants. - public final static int VALUE_MASK = 0x7f; - public final static int NEXT_BIT_SHIFT = 7; - public final static int NEXT_BIT_MASK = 1 << 7; - final static boolean UNSAFE_AVAIL = HBasePlatformDependent.isUnsafeAvailable(); - public final static boolean UNSAFE_UNALIGNED = HBasePlatformDependent.unaligned(); - - private ByteBufferUtils() { - } - - static abstract class Comparer { - - abstract int compareTo(byte[] buf1, int o1, int l1, ByteBuffer buf2, int o2, int l2); - - abstract int compareTo(ByteBuffer buf1, int o1, int l1, ByteBuffer buf2, int o2, int l2); - } - - static abstract class Converter { - - abstract short toShort(ByteBuffer buffer, int offset); - - abstract int toInt(ByteBuffer buffer); - - abstract int toInt(ByteBuffer buffer, int offset); - - abstract long toLong(ByteBuffer buffer, int offset); - - abstract void putInt(ByteBuffer buffer, int val); - - abstract int putInt(ByteBuffer buffer, int index, int val); - - abstract void putShort(ByteBuffer buffer, short val); - - abstract int putShort(ByteBuffer buffer, int index, short val); - - abstract void putLong(ByteBuffer buffer, long val); - - abstract int putLong(ByteBuffer buffer, int index, long val); - } - - static abstract class CommonPrefixer { - - abstract int findCommonPrefix(ByteBuffer left, int leftOffset, int leftLength, byte[] right, - int rightOffset, int rightLength); - - abstract int findCommonPrefix(ByteBuffer left, int leftOffset, int leftLength, ByteBuffer right, - int rightOffset, int rightLength); - } - - static class ComparerHolder { - - static final String UNSAFE_COMPARER_NAME = ComparerHolder.class.getName() + "$UnsafeComparer"; - - static final Comparer BEST_COMPARER = getBestComparer(); - - static Comparer getBestComparer() { - try { - final Class theClass = - Class.forName(UNSAFE_COMPARER_NAME).asSubclass(Comparer.class); - - return theClass.getConstructor().newInstance(); - } catch (final Throwable t) { // ensure we really catch *everything* - return PureJavaComparer.INSTANCE; - } - } - - static final class PureJavaComparer extends Comparer { - - static final PureJavaComparer INSTANCE = new PureJavaComparer(); - - private PureJavaComparer() { - } - - @Override - public int compareTo(final byte[] buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - final int end1 = o1 + l1; - final int end2 = o2 + l2; - for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) { - final int a = buf1[i] & 0xFF; - final int b = buf2.get(j) & 0xFF; - if (a != b) { - return a - b; - } - } - return l1 - l2; - } - - @Override - public int compareTo(final ByteBuffer buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - final int end1 = o1 + l1; - final int end2 = o2 + l2; - for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) { - final int a = buf1.get(i) & 0xFF; - final int b = buf2.get(j) & 0xFF; - if (a != b) { - return a - b; - } - } - return l1 - l2; - } - } - - static final class UnsafeComparer extends Comparer { - - public UnsafeComparer() { - } - - static { - if (!UNSAFE_UNALIGNED) { - throw new Error(); - } - } - - @Override - public int compareTo(final byte[] buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - final long offset2Adj; - Object refObj2 = null; - if (buf2.isDirect()) { - offset2Adj = o2 + UnsafeAccess.directBufferAddress(buf2); - } else { - offset2Adj = o2 + buf2.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - refObj2 = buf2.array(); - } - return compareToUnsafe(buf1, o1 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, l1, refObj2, - offset2Adj, l2); - } - - @Override - public int compareTo(final ByteBuffer buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - final long offset1Adj; - final long offset2Adj; - Object refObj1 = null, refObj2 = null; - if (buf1.isDirect()) { - offset1Adj = o1 + UnsafeAccess.directBufferAddress(buf1); - } else { - offset1Adj = o1 + buf1.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - refObj1 = buf1.array(); - } - if (buf2.isDirect()) { - offset2Adj = o2 + UnsafeAccess.directBufferAddress(buf2); - } else { - offset2Adj = o2 + buf2.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - refObj2 = buf2.array(); - } - return compareToUnsafe(refObj1, offset1Adj, l1, refObj2, offset2Adj, l2); - } - } - } - - static class ConverterHolder { - - static final String UNSAFE_CONVERTER_NAME = - ConverterHolder.class.getName() + "$UnsafeConverter"; - static final Converter BEST_CONVERTER = getBestConverter(); - - static Converter getBestConverter() { - try { - final Class theClass = - Class.forName(UNSAFE_CONVERTER_NAME).asSubclass(Converter.class); - - // yes, UnsafeComparer does implement Comparer - return theClass.getConstructor().newInstance(); - } catch (final Throwable t) { // ensure we really catch *everything* - return PureJavaConverter.INSTANCE; - } - } - - static final class PureJavaConverter extends Converter { - - static final PureJavaConverter INSTANCE = new PureJavaConverter(); - - private PureJavaConverter() { - } - - @Override - short toShort(final ByteBuffer buffer, final int offset) { - return buffer.getShort(offset); - } - - @Override - int toInt(final ByteBuffer buffer) { - return buffer.getInt(); - } - - @Override - int toInt(final ByteBuffer buffer, final int offset) { - return buffer.getInt(offset); - } - - @Override - long toLong(final ByteBuffer buffer, final int offset) { - return buffer.getLong(offset); - } - - @Override - void putInt(final ByteBuffer buffer, final int val) { - buffer.putInt(val); - } - - @Override - int putInt(final ByteBuffer buffer, final int index, final int val) { - buffer.putInt(index, val); - return index + Bytes.SIZEOF_INT; - } - - @Override - void putShort(final ByteBuffer buffer, final short val) { - buffer.putShort(val); - } - - @Override - int putShort(final ByteBuffer buffer, final int index, final short val) { - buffer.putShort(index, val); - return index + Bytes.SIZEOF_SHORT; - } - - @Override - void putLong(final ByteBuffer buffer, final long val) { - buffer.putLong(val); - } - - @Override - int putLong(final ByteBuffer buffer, final int index, final long val) { - buffer.putLong(index, val); - return index + Bytes.SIZEOF_LONG; - } - } - - static final class UnsafeConverter extends Converter { - - public UnsafeConverter() { - } - - static { - if (!UNSAFE_UNALIGNED) { - throw new Error(); - } - } - - @Override - short toShort(final ByteBuffer buffer, final int offset) { - return UnsafeAccess.toShort(buffer, offset); - } - - @Override - int toInt(final ByteBuffer buffer) { - final int i = UnsafeAccess.toInt(buffer, buffer.position()); - buffer.position(buffer.position() + Bytes.SIZEOF_INT); - return i; - } - - @Override - int toInt(final ByteBuffer buffer, final int offset) { - return UnsafeAccess.toInt(buffer, offset); - } - - @Override - long toLong(final ByteBuffer buffer, final int offset) { - return UnsafeAccess.toLong(buffer, offset); - } - - @Override - void putInt(final ByteBuffer buffer, final int val) { - final int newPos = UnsafeAccess.putInt(buffer, buffer.position(), val); - buffer.position(newPos); - } - - @Override - int putInt(final ByteBuffer buffer, final int index, final int val) { - return UnsafeAccess.putInt(buffer, index, val); - } - - @Override - void putShort(final ByteBuffer buffer, final short val) { - final int newPos = UnsafeAccess.putShort(buffer, buffer.position(), val); - buffer.position(newPos); - } - - @Override - int putShort(final ByteBuffer buffer, final int index, final short val) { - return UnsafeAccess.putShort(buffer, index, val); - } - - @Override - void putLong(final ByteBuffer buffer, final long val) { - final int newPos = UnsafeAccess.putLong(buffer, buffer.position(), val); - buffer.position(newPos); - } - - @Override - int putLong(final ByteBuffer buffer, final int index, final long val) { - return UnsafeAccess.putLong(buffer, index, val); - } - } - } - - static class CommonPrefixerHolder { - - static final String UNSAFE_COMMON_PREFIXER_NAME = - CommonPrefixerHolder.class.getName() + "$UnsafeCommonPrefixer"; - - static final CommonPrefixer BEST_COMMON_PREFIXER = getBestCommonPrefixer(); - - static CommonPrefixer getBestCommonPrefixer() { - try { - final Class theClass = - Class.forName(UNSAFE_COMMON_PREFIXER_NAME).asSubclass(CommonPrefixer.class); - - return theClass.getConstructor().newInstance(); - } catch (final Throwable t) { // ensure we really catch *everything* - return PureJavaCommonPrefixer.INSTANCE; - } - } - - static final class PureJavaCommonPrefixer extends CommonPrefixer { - - static final PureJavaCommonPrefixer INSTANCE = new PureJavaCommonPrefixer(); - - private PureJavaCommonPrefixer() { - } - - @Override - public int findCommonPrefix(final ByteBuffer left, final int leftOffset, final int leftLength, final byte[] right, - final int rightOffset, final int rightLength) { - final int length = Math.min(leftLength, rightLength); - int result = 0; - - while ( - result < length - && ByteBufferUtils.toByte(left, leftOffset + result) == right[rightOffset + result] - ) { - result++; - } - - return result; - } - - @Override - int findCommonPrefix(final ByteBuffer left, final int leftOffset, final int leftLength, final ByteBuffer right, - final int rightOffset, final int rightLength) { - final int length = Math.min(leftLength, rightLength); - int result = 0; - - while ( - result < length && ByteBufferUtils.toByte(left, leftOffset + result) - == ByteBufferUtils.toByte(right, rightOffset + result) - ) { - result++; - } - - return result; - } - } - - static final class UnsafeCommonPrefixer extends CommonPrefixer { - - static { - if (!UNSAFE_UNALIGNED) { - throw new Error(); - } - } - - public UnsafeCommonPrefixer() { - } - - @Override - public int findCommonPrefix(final ByteBuffer left, final int leftOffset, final int leftLength, final byte[] right, - final int rightOffset, final int rightLength) { - final long offset1Adj; - Object refObj1 = null; - if (left.isDirect()) { - offset1Adj = leftOffset + UnsafeAccess.directBufferAddress(left); - } else { - offset1Adj = leftOffset + left.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - refObj1 = left.array(); - } - return findCommonPrefixUnsafe(refObj1, offset1Adj, leftLength, right, - rightOffset + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, rightLength); - } - - @Override - public int findCommonPrefix(final ByteBuffer left, final int leftOffset, final int leftLength, final ByteBuffer right, - final int rightOffset, final int rightLength) { - final long offset1Adj; - final long offset2Adj; - Object refObj1 = null, refObj2 = null; - if (left.isDirect()) { - offset1Adj = leftOffset + UnsafeAccess.directBufferAddress(left); - } else { - offset1Adj = leftOffset + left.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - refObj1 = left.array(); - } - if (right.isDirect()) { - offset2Adj = rightOffset + UnsafeAccess.directBufferAddress(right); - } else { - offset2Adj = rightOffset + right.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - refObj2 = right.array(); - } - return findCommonPrefixUnsafe(refObj1, offset1Adj, leftLength, refObj2, offset2Adj, - rightLength); - } - } - } - - /** - * Similar to {@link WritableUtils#writeVLong(java.io.DataOutput, long)}, but writes to a - * {@link ByteBuffer}. - */ - public static void writeVLong(final ByteBuffer out, long i) { - if (i >= -112 && i <= 127) { - out.put((byte) i); - return; - } - - int len = -112; - if (i < 0) { - i ^= -1L; // take one's complement - len = -120; - } - - long tmp = i; - while (tmp != 0) { - tmp = tmp >> 8; - len--; - } - - out.put((byte) len); - - len = (len < -120) - ? -(len + 120) - : -(len + 112); - - for (int idx = len; idx != 0; idx--) { - final int shiftbits = (idx - 1) * 8; - final long mask = 0xFFL << shiftbits; - out.put((byte) ((i & mask) >> shiftbits)); - } - } - -// /** -// * Similar to {@link WritableUtils#readVLong(java.io.DataInput)} but reads from a -// * {@link ByteBuff}. -// */ -// public static long readVLong(ByteBuff buf) { -// byte firstByte = buf.get(); -// int len = WritableUtils.decodeVIntSize(firstByte); -// if (len == 1) { -// return firstByte; -// } else { -// int remaining = len - 1; -// long i = 0; -// int offsetFromPos = 0; -// if (remaining >= Bytes.SIZEOF_INT) { -// // The int read has to be converted to unsigned long so the & op -// i = (buf.getIntAfterPosition(offsetFromPos) & 0x00000000ffffffffL); -// remaining -= Bytes.SIZEOF_INT; -// offsetFromPos += Bytes.SIZEOF_INT; -// } -// if (remaining >= Bytes.SIZEOF_SHORT) { -// short s = buf.getShortAfterPosition(offsetFromPos); -// i = i << 16; -// i = i | (s & 0xFFFF); -// remaining -= Bytes.SIZEOF_SHORT; -// offsetFromPos += Bytes.SIZEOF_SHORT; -// } -// for (int idx = 0; idx < remaining; idx++) { -// byte b = buf.getByteAfterPosition(offsetFromPos + idx); -// i = i << 8; -// i = i | (b & 0xFF); -// } -// buf.skip(len - 1); -// return WritableUtils.isNegativeVInt(firstByte) -// ? ~i -// : i; -// } -// } - - /** - * Similar to {@link WritableUtils#readVLong(DataInput)} but reads from a {@link ByteBuffer}. - */ - public static long readVLong(final ByteBuffer buf) { - final byte firstByte = buf.get(); - final int len = WritableUtils.decodeVIntSize(firstByte); - if (len == 1) { - return firstByte; - } else { - int remaining = len - 1; - long i = 0; - int offsetFromPos = 0; - if (remaining >= Bytes.SIZEOF_INT) { - // The int read has to be converted to unsigned long so the & op - i = (buf.getInt(buf.position() + offsetFromPos) & 0x00000000ffffffffL); - remaining -= Bytes.SIZEOF_INT; - offsetFromPos += Bytes.SIZEOF_INT; - } - if (remaining >= Bytes.SIZEOF_SHORT) { - final short s = buf.getShort(buf.position() + offsetFromPos); - i = i << 16; - i = i | (s & 0xFFFF); - remaining -= Bytes.SIZEOF_SHORT; - offsetFromPos += Bytes.SIZEOF_SHORT; - } - for (int idx = 0; idx < remaining; idx++) { - final byte b = buf.get(buf.position() + offsetFromPos + idx); - i = i << 8; - i = i | (b & 0xFF); - } - buf.position(buf.position() + len - 1); - return WritableUtils.isNegativeVInt(firstByte) - ? ~i - : i; - } - } - - /** - * Put in buffer integer using 7 bit encoding. For each written byte: 7 bits are used to store - * value 1 bit is used to indicate whether there is next bit. - * - * @param value Int to be compressed. - * @param out Where to put compressed data - * @return Number of bytes written. - * @throws IOException on stream error - */ - public static int putCompressedInt(final OutputStream out, final int value) throws IOException { - int i = 0; - int tmpvalue = value; - do { - byte b = (byte) (tmpvalue & VALUE_MASK); - tmpvalue >>>= NEXT_BIT_SHIFT; - if (tmpvalue != 0) { - b |= (byte) NEXT_BIT_MASK; - } - out.write(b); - i++; - } while (tmpvalue != 0); - return i; - } - -// /** -// * Put in output stream 32 bit integer (Big Endian byte order). -// * -// * @param out Where to put integer. -// * @param value Value of integer. -// * @throws IOException On stream error. -// */ -// public static void putInt(OutputStream out, final int value) throws IOException { -// // We have writeInt in ByteBufferOutputStream so that it can directly write -// // int to underlying -// // ByteBuffer in one step. -// if (out instanceof ByteBufferWriter) { -// ((ByteBufferWriter) out).writeInt(value); -// } else { -// StreamUtils.writeInt(out, value); -// } -// } - - public static byte toByte(final ByteBuffer buffer, final int offset) { - if (UNSAFE_AVAIL) { - return UnsafeAccess.toByte(buffer, offset); - } else { - return buffer.get(offset); - } - } - -// /** -// * Copy the data to the output stream and update position in buffer. -// * -// * @param out the stream to write bytes to -// * @param in the buffer to read bytes from -// * @param length the number of bytes to copy -// */ -// public static void moveBufferToStream(OutputStream out, ByteBuffer in, int length) -// throws IOException { -// copyBufferToStream(out, in, in.position(), length); -// skip(in, length); -// } - -// /** -// * Copy data from a buffer to an output stream. Does not update the position in the buffer. -// * -// * @param out the stream to write bytes to -// * @param in the buffer to read bytes from -// * @param offset the offset in the buffer (from the buffer's array offset) to start copying bytes -// * from -// * @param length the number of bytes to copy -// */ -// public static void copyBufferToStream(OutputStream out, ByteBuffer in, int offset, int length) -// throws IOException { -// if (out instanceof ByteBufferWriter) { -// ((ByteBufferWriter) out).write(in, offset, length); -// } else if (in.hasArray()) { -// out.write(in.array(), in.arrayOffset() + offset, length); -// } else { -// for (int i = 0; i < length; ++i) { -// out.write(toByte(in, offset + i)); -// } -// } -// } - -// /** -// * Copy data from a buffer to an output stream. Does not update the position in the buffer. -// * -// * @param out the output stream to write bytes to -// * @param in the buffer to read bytes from -// * @param offset the offset in the buffer (from the buffer's array offset) to start copying bytes -// * from -// * @param length the number of bytes to copy -// */ -// public static void copyBufferToStream(DataOutput out, ByteBuffer in, int offset, int length) -// throws IOException { -// if (out instanceof ByteBufferWriter) { -// ((ByteBufferWriter) out).write(in, offset, length); -// } else if (in.hasArray()) { -// out.write(in.array(), in.arrayOffset() + offset, length); -// } else { -// for (int i = 0; i < length; ++i) { -// out.write(toByte(in, offset + i)); -// } -// } -// } - - public static int putLong(final OutputStream out, final long value, final int fitInBytes) - throws IOException { - long tmpValue = value; - for (int i = 0; i < fitInBytes; ++i) { - out.write((byte) (tmpValue & 0xff)); - tmpValue >>>= 8; - } - return fitInBytes; - } - - public static int putByte(final ByteBuffer buffer, final int offset, final byte b) { - if (UNSAFE_AVAIL) { - return UnsafeAccess.putByte(buffer, offset, b); - } else { - buffer.put(offset, b); - return offset + 1; - } - } - - /** - * Check how many bytes are required to store value. - * - * @param value Value which size will be tested. - * @return How many bytes are required to store value. - */ - public static int longFitsIn(final long value) { - if (value < 0) { - return 8; - } - - if (value < (1L << (4 * 8))) { - // no more than 4 bytes - if (value < (1L << (2 * 8))) { - if (value < (1L << (1 * 8))) { - return 1; - } - return 2; - } - if (value < (1L << (3 * 8))) { - return 3; - } - return 4; - } - // more than 4 bytes - if (value < (1L << (6 * 8))) { - if (value < (1L << (5 * 8))) { - return 5; - } - return 6; - } - if (value < (1L << (7 * 8))) { - return 7; - } - return 8; - } - - /** - * Check how many bytes is required to store value. - * - * @param value Value which size will be tested. - * @return How many bytes are required to store value. - */ - public static int intFitsIn(final int value) { - if (value < 0) { - return 4; - } - - if (value < (1 << (2 * 8))) { - if (value < (1 << (1 * 8))) { - return 1; - } - return 2; - } - if (value <= (1 << (3 * 8))) { - return 3; - } - return 4; - } - - /** - * Read integer from stream coded in 7 bits and increment position. - * - * @return the integer that has been read - * @throws IOException on stream error - */ - public static int readCompressedInt(final InputStream input) throws IOException { - int result = 0; - int i = 0; - byte b; - do { - b = (byte) input.read(); - result += (b & VALUE_MASK) << (NEXT_BIT_SHIFT * i); - i++; - if (i > Bytes.SIZEOF_INT + 1) { - throw new IllegalStateException( - "Corrupted compressed int (too long: " + (i + 1) + " bytes)"); - } - } while (0 != (b & NEXT_BIT_MASK)); - return result; - } - - /** - * Read integer from buffer coded in 7 bits and increment position. - * - * @return Read integer. - */ - public static int readCompressedInt(final ByteBuffer buffer) { - final byte b = buffer.get(); - if ((b & NEXT_BIT_MASK) != 0) { - return (b & VALUE_MASK) + (readCompressedInt(buffer) << NEXT_BIT_SHIFT); - } - return b & VALUE_MASK; - } - - /** - * Read long which was written to fitInBytes bytes and increment position. - * - * @param fitInBytes In how many bytes given long is stored. - * @return The value of parsed long. - * @throws IOException on stream error - */ - public static long readLong(final InputStream in, final int fitInBytes) throws IOException { - long tmpLong = 0; - for (int i = 0; i < fitInBytes; ++i) { - tmpLong |= (in.read() & 0xffL) << (8 * i); - } - return tmpLong; - } - - /** - * Read long which was written to fitInBytes bytes and increment position. - * - * @param fitInBytes In how many bytes given long is stored. - * @return The value of parsed long. - */ - public static long readLong(final ByteBuffer in, final int fitInBytes) { - long tmpLength = 0; - for (int i = 0; i < fitInBytes; ++i) { - tmpLength |= (in.get() & 0xffL) << (8L * i); - } - return tmpLength; - } - - /** - * Copy the given number of bytes from the given stream and put it at the current position of the - * given buffer, updating the position in the buffer. - * - * @param out the buffer to write data to - * @param in the stream to read data from - * @param length the number of bytes to read/write - */ - public static void copyFromStreamToBuffer(final ByteBuffer out, final DataInputStream in, final int length) - throws IOException { - if (out.hasArray()) { - in.readFully(out.array(), out.position() + out.arrayOffset(), length); - skip(out, length); - } else { - for (int i = 0; i < length; ++i) { - out.put(in.readByte()); - } - } - } - - /** - * Copy from the InputStream to a new heap ByteBuffer until the InputStream is exhausted. - */ - public static ByteBuffer drainInputStreamToBuffer(final InputStream is) throws IOException { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); - IOUtils.copyBytes(is, baos, 4096, true); - final ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray()); - buffer.rewind(); - return buffer; - } - - /** - * Copy one buffer's whole data to another. Write starts at the current position of 'out' buffer. - * Note : This will advance the position marker of {@code out} and also change the position maker - * for {@code in}. - * - * @param in source buffer - * @param out destination buffer - */ - public static void copyFromBufferToBuffer(final ByteBuffer in, final ByteBuffer out) { - if (in.hasArray() && out.hasArray()) { - final int length = in.remaining(); - System.arraycopy(in.array(), in.arrayOffset(), out.array(), out.arrayOffset(), length); - out.position(out.position() + length); - in.position(in.limit()); - } else if (UNSAFE_AVAIL) { - final int length = in.remaining(); - UnsafeAccess.copy(in, in.position(), out, out.position(), length); - out.position(out.position() + length); - in.position(in.limit()); - } else { - out.put(in); - } - } - - /** - * Copy from one buffer to another from given offset. This will be absolute positional copying and - * won't affect the position of any of the buffers. - * - * @param in input bytebuffer - * @param out destination bytebuffer - * @param sourceOffset offset of source buffer - * @param destinationOffset offset of destination buffer - * @param length the number of bytes to copy - */ - public static void copyFromBufferToBuffer(final ByteBuffer in, final ByteBuffer out, final int sourceOffset, - final int destinationOffset, final int length) { - if (in.hasArray() && out.hasArray()) { - System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out.array(), - out.arrayOffset() + destinationOffset, length); - } else if (UNSAFE_AVAIL) { - UnsafeAccess.copy(in, sourceOffset, out, destinationOffset, length); - } else { - final ByteBuffer outDup = out.duplicate(); - outDup.position(destinationOffset); - final ByteBuffer inDup = in.duplicate(); - inDup.position(sourceOffset).limit(sourceOffset + length); - outDup.put(inDup); - } - // We used to return a result but disabled; return destinationOffset + length; - } - - /** - * Copy from one buffer to another from given offset. - *

- * Note : This will advance the position marker of {@code out} but not change the position maker - * for {@code in} - * - * @param in source buffer - * @param out destination buffer - * @param sourceOffset offset in the source buffer - * @param length how many bytes to copy - */ - public static void copyFromBufferToBuffer(final ByteBuffer in, final ByteBuffer out, final int sourceOffset, - final int length) { - if (in.hasArray() && out.hasArray()) { - System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out.array(), - out.position() + out.arrayOffset(), length); - skip(out, length); - } else if (UNSAFE_AVAIL) { - UnsafeAccess.copy(in, sourceOffset, out, out.position(), length); - skip(out, length); - } else { - final ByteBuffer inDup = in.duplicate(); - inDup.position(sourceOffset).limit(sourceOffset + length); - out.put(inDup); - } - } - - /** - * Find length of common prefix in two arrays. - * - * @param left Array to be compared. - * @param leftOffset Offset in left array. - * @param leftLength Length of left array. - * @param right Array to be compared. - * @param rightOffset Offset in right array. - * @param rightLength Length of right array. - */ - public static int findCommonPrefix(final byte[] left, final int leftOffset, final int leftLength, final byte[] right, - final int rightOffset, final int rightLength) { - return Bytes.findCommonPrefix(left, right, leftLength, rightLength, leftOffset, rightOffset); - } - - /** - * Find length of common prefix in two arrays. - * - * @param left ByteBuffer to be compared. - * @param leftOffset Offset in left ByteBuffer. - * @param leftLength Length of left ByteBuffer. - * @param right ByteBuffer to be compared. - * @param rightOffset Offset in right ByteBuffer. - * @param rightLength Length of right ByteBuffer. - */ - public static int findCommonPrefix(final ByteBuffer left, final int leftOffset, final int leftLength, - final ByteBuffer right, final int rightOffset, final int rightLength) { - return CommonPrefixerHolder.BEST_COMMON_PREFIXER.findCommonPrefix(left, leftOffset, leftLength, - right, rightOffset, rightLength); - } - - /** - * Find length of common prefix in two arrays. - * - * @param left ByteBuffer to be compared. - * @param leftOffset Offset in left ByteBuffer. - * @param leftLength Length of left ByteBuffer. - * @param right Array to be compared - * @param rightOffset Offset in right Array. - * @param rightLength Length of right Array. - */ - public static int findCommonPrefix(final ByteBuffer left, final int leftOffset, final int leftLength, final byte[] right, - final int rightOffset, final int rightLength) { - return CommonPrefixerHolder.BEST_COMMON_PREFIXER.findCommonPrefix(left, leftOffset, leftLength, - right, rightOffset, rightLength); - } - - /** - * Check whether two parts in the same buffer are equal. - * - * @param buffer In which buffer there are parts - * @param offsetLeft Beginning of first part. - * @param lengthLeft Length of the first part. - * @param offsetRight Beginning of the second part. - * @param lengthRight Length of the second part. - * @return True if equal - */ - public static boolean arePartsEqual(final ByteBuffer buffer, final int offsetLeft, final int lengthLeft, - final int offsetRight, final int lengthRight) { - if (lengthLeft != lengthRight) { - return false; - } - - if (buffer.hasArray()) { - return 0 == Bytes.compareTo(buffer.array(), buffer.arrayOffset() + offsetLeft, lengthLeft, - buffer.array(), buffer.arrayOffset() + offsetRight, lengthRight); - } - - for (int i = 0; i < lengthRight; ++i) { - if (buffer.get(offsetLeft + i) != buffer.get(offsetRight + i)) { - return false; - } - } - return true; - } - - /** - * Increment position in buffer. - * - * @param buffer In this buffer. - * @param length By that many bytes. - */ - public static void skip(final ByteBuffer buffer, final int length) { - buffer.position(buffer.position() + length); - } - - public static void extendLimit(final ByteBuffer buffer, final int numBytes) { - buffer.limit(buffer.limit() + numBytes); - } - - /** - * Copy the bytes from position to limit into a new byte[] of the exact length and sets the - * position and limit back to their original values (though not thread safe). - * - * @param buffer copy from here - * @param startPosition put buffer.get(startPosition) into byte[0] - * @return a new byte[] containing the bytes in the specified range - */ - public static byte[] toBytes(final ByteBuffer buffer, final int startPosition) { - final int originalPosition = buffer.position(); - final byte[] output = new byte[buffer.limit() - startPosition]; - buffer.position(startPosition); - buffer.get(output); - buffer.position(originalPosition); - return output; - } - - /** - * Copy the given number of bytes from specified offset into a new byte[] - * - * @param buffer input bytebuffer to read - * @param offset input offset where Bytes are - * @param length the number of bytes to read - * @return a new byte[] containing the bytes in the specified range - */ - public static byte[] toBytes(final ByteBuffer buffer, final int offset, final int length) { - final byte[] output = new byte[length]; - for (int i = 0; i < length; i++) { - output[i] = buffer.get(offset + i); - } - return output; - } - - public static boolean equals(final ByteBuffer buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - if ((l1 == 0) || (l2 == 0)) { - // both 0 length, return true, or else false - return l1 == l2; - } - // Since we're often comparing adjacent sorted data, - // it's usual to have equal arrays except for the very last byte - // so check that first - if (toByte(buf1, o1 + l1 - 1) != toByte(buf2, o2 + l2 - 1)) { - return false; - } - return compareTo(buf1, o1, l1, buf2, o2, l2) == 0; - } - - /** - * ByteBuffer to hash offset to start from length to hash - */ - public static int hashCode(final ByteBuffer buf, final int offset, final int length) { - int hash = 1; - for (int i = offset; i < offset + length; i++) { - hash = (31 * hash) + (int) toByte(buf, i); - } - return hash; - } - - public static int compareTo(final ByteBuffer buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - return ComparerHolder.BEST_COMPARER.compareTo(buf1, o1, l1, buf2, o2, l2); - } - - public static boolean equals(final ByteBuffer buf1, final int o1, final int l1, final byte[] buf2, final int o2, final int l2) { - if ((l1 == 0) || (l2 == 0)) { - // both 0 length, return true, or else false - return l1 == l2; - } - // Since we're often comparing adjacent sorted data, - // it's usual to have equal arrays except for the very last byte - // so check that first - if (toByte(buf1, o1 + l1 - 1) != buf2[o2 + l2 - 1]) { - return false; - } - return compareTo(buf1, o1, l1, buf2, o2, l2) == 0; - } - - // The below two methods show up in lots of places. Versions of them in commons util and in - // Cassandra. In guava too? They are copied from ByteBufferUtils. They are here as static - // privates. Seems to make code smaller and make Hotspot happier (comes of compares and study - // of compiled code via jitwatch). - - public static int compareTo(final byte[] buf1, final int o1, final int l1, final ByteBuffer buf2, final int o2, final int l2) { - return ComparerHolder.BEST_COMPARER.compareTo(buf1, o1, l1, buf2, o2, l2); - } - - public static int compareTo(final ByteBuffer buf1, final int o1, final int l1, final byte[] buf2, final int o2, final int l2) { - return compareTo(buf2, o2, l2, buf1, o1, l1) * -1; - } - - static int compareToUnsafe(final Object obj1, final long o1, final int l1, final Object obj2, final long o2, final int l2) { - final int stride = 8; - final int minLength = Math.min(l1, l2); - final int strideLimit = minLength & ~(stride - 1); - int i; - - /* - * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a time is no slower than - * comparing 4 bytes at a time even on 32-bit. On the other hand, it is substantially faster on - * 64-bit. - */ - for (i = 0; i < strideLimit; i += stride) { - final long lw = HBasePlatformDependent.getLong(obj1, o1 + (long) i); - final long rw = HBasePlatformDependent.getLong(obj2, o2 + (long) i); - if (lw != rw) { - if (!UnsafeAccess.LITTLE_ENDIAN) { - return ((lw + Long.MIN_VALUE) < (rw + Long.MIN_VALUE)) - ? -1 - : 1; - } - - /* - * We want to compare only the first index where left[index] != right[index]. This - * corresponds to the least significant nonzero byte in lw ^ rw, since lw and rw are - * little-endian. Long.numberOfTrailingZeros(diff) tells us the least significant nonzero - * bit, and zeroing out the first three bits of L.nTZ gives us the shift to get that least - * significant nonzero byte. This comparison logic is based on UnsignedBytes from guava v21 - */ - final int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; - return ((int) ((lw >>> n) & 0xFF)) - ((int) ((rw >>> n) & 0xFF)); - } - } - - // The epilogue to cover the last (minLength % stride) elements. - for (; i < minLength; i++) { - final int il = (HBasePlatformDependent.getByte(obj1, o1 + i) & 0xFF); - final int ir = (HBasePlatformDependent.getByte(obj2, o2 + i) & 0xFF); - if (il != ir) { - return il - ir; - } - } - return l1 - l2; - } - - static int findCommonPrefixUnsafe(final Object left, final long leftOffset, final int leftLength, final Object right, - final long rightOffset, final int rightLength) { - final int stride = 8; - final int minLength = Math.min(leftLength, rightLength); - final int strideLimit = minLength & ~(stride - 1); - int result = 0; - int i; - - for (i = 0; i < strideLimit; i += stride) { - final long lw = HBasePlatformDependent.getLong(left, leftOffset + (long) i); - final long rw = HBasePlatformDependent.getLong(right, rightOffset + (long) i); - - if (lw != rw) { - if (!UnsafeAccess.LITTLE_ENDIAN) { - return result + (Long.numberOfLeadingZeros(lw ^ rw) / Bytes.SIZEOF_LONG); - } else { - return result + (Long.numberOfTrailingZeros(lw ^ rw) / Bytes.SIZEOF_LONG); - } - } else { - result += Bytes.SIZEOF_LONG; - } - } - - // The epilogue to cover the last (minLength % stride) elements. - for (; i < minLength; i++) { - final byte il = HBasePlatformDependent.getByte(left, leftOffset + i); - final byte ir = HBasePlatformDependent.getByte(right, rightOffset + i); - if (il != ir) { - return result; - } else { - result++; - } - } - - return result; - } - - /** - * Reads a short value at the given buffer's offset. - * - * @param buffer input byte buffer to read - * @param offset input offset where short is - * @return short value at offset - */ - public static short toShort(final ByteBuffer buffer, final int offset) { - return ConverterHolder.BEST_CONVERTER.toShort(buffer, offset); - } - - /** - * Reads an int value at the given buffer's current position. Also advances the buffer's position - */ - public static int toInt(final ByteBuffer buffer) { - return ConverterHolder.BEST_CONVERTER.toInt(buffer); - } - - /** - * Reads an int value at the given buffer's offset. - * - * @param buffer input byte buffer to read - * @param offset input offset where int is - * @return int value at offset - */ - public static int toInt(final ByteBuffer buffer, final int offset) { - return ConverterHolder.BEST_CONVERTER.toInt(buffer, offset); - } - - /** - * Converts a ByteBuffer to an int value - * - * @param buf The ByteBuffer - * @param offset Offset to int value - * @param length Number of bytes used to store the int value. - * @return the int value if there's not enough bytes left in the buffer after the given offset - */ - public static int readAsInt(final ByteBuffer buf, final int offset, final int length) { - if (offset + length > buf.limit()) { - throw new IllegalArgumentException("offset (" + offset + ") + length (" + length - + ") exceed the" + " limit of the buffer: " + buf.limit()); - } - int n = 0; - for (int i = offset; i < (offset + length); i++) { - n <<= 8; - n ^= toByte(buf, i) & 0xFF; - } - return n; - } - - /** - * Reads a long value at the given buffer's offset. - * - * @param buffer input byte buffer to read - * @param offset input offset where Long is - * @return long value at offset - */ - public static long toLong(final ByteBuffer buffer, final int offset) { - return ConverterHolder.BEST_CONVERTER.toLong(buffer, offset); - } - - /** - * Put an int value out to the given ByteBuffer's current position in big-endian format. This also - * advances the position in buffer by int size. - * - * @param buffer the ByteBuffer to write to - * @param val int to write out - */ - public static void putInt(final ByteBuffer buffer, final int val) { - ConverterHolder.BEST_CONVERTER.putInt(buffer, val); - } - - public static int putInt(final ByteBuffer buffer, final int index, final int val) { - return ConverterHolder.BEST_CONVERTER.putInt(buffer, index, val); - } - - /** - * Reads a double value at the given buffer's offset. - * - * @param buffer input byte buffer to read - * @param offset offset where double is - * @return double value at offset - */ - public static double toDouble(final ByteBuffer buffer, final int offset) { - return Double.longBitsToDouble(toLong(buffer, offset)); - } - - /** - * Reads a BigDecimal value at the given buffer's offset. - * - * @param buffer input bytebuffer to read - * @param offset input offset - * @return BigDecimal value at offset - */ - public static BigDecimal toBigDecimal(final ByteBuffer buffer, final int offset, final int length) { - if (buffer == null || length < Bytes.SIZEOF_INT + 1 || (offset + length > buffer.limit())) { - return null; - } - - final int scale = toInt(buffer, offset); - final byte[] tcBytes = new byte[length - Bytes.SIZEOF_INT]; - copyFromBufferToArray(tcBytes, buffer, offset + Bytes.SIZEOF_INT, 0, length - Bytes.SIZEOF_INT); - return new BigDecimal(new BigInteger(tcBytes), scale); - } - - /** - * Put a short value out to the given ByteBuffer's current position in big-endian format. This - * also advances the position in buffer by short size. - * - * @param buffer the ByteBuffer to write to - * @param val short to write out - */ - public static void putShort(final ByteBuffer buffer, final short val) { - ConverterHolder.BEST_CONVERTER.putShort(buffer, val); - } - - public static int putShort(final ByteBuffer buffer, final int index, final short val) { - return ConverterHolder.BEST_CONVERTER.putShort(buffer, index, val); - } - - public static int putAsShort(final ByteBuffer buf, final int index, int val) { - buf.put(index + 1, (byte) val); - val >>= 8; - buf.put(index, (byte) val); - return index + Bytes.SIZEOF_SHORT; - } - - /** - * Put a long value out to the given ByteBuffer's current position in big-endian format. This also - * advances the position in buffer by long size. - * - * @param buffer the ByteBuffer to write to - * @param val long to write out - */ - public static void putLong(final ByteBuffer buffer, final long val) { - ConverterHolder.BEST_CONVERTER.putLong(buffer, val); - } - - public static int putLong(final ByteBuffer buffer, final int index, final long val) { - return ConverterHolder.BEST_CONVERTER.putLong(buffer, index, val); - } - - /** - * Copies the bytes from given array's offset to length part into the given buffer. Puts the bytes - * to buffer's current position. This also advances the position in the 'out' buffer by 'length' - * - * @param out output bytebuffer to copy to - * @param in input array to copy from - * @param inOffset input offset to copy from - * @param length the number of bytes to copy - */ - public static void copyFromArrayToBuffer(final ByteBuffer out, final byte[] in, final int inOffset, final int length) { - if (out.hasArray()) { - System.arraycopy(in, inOffset, out.array(), out.arrayOffset() + out.position(), length); - // Move the position in out by length - out.position(out.position() + length); - } else if (UNSAFE_AVAIL) { - UnsafeAccess.copy(in, inOffset, out, out.position(), length); - // Move the position in out by length - out.position(out.position() + length); - } else { - out.put(in, inOffset, length); - } - } - - /** - * Copies bytes from given array's offset to length part into the given buffer. Puts the bytes to - * buffer's given position. This doesn't affect the position of buffer. - * - * @param out output bytebuffer to copy to - * @param outOffset output buffer offset - * @param in input array to copy from - * @param inOffset input offset to copy from - * @param length the number of bytes to copy - */ - public static void copyFromArrayToBuffer(final ByteBuffer out, final int outOffset, final byte[] in, final int inOffset, - final int length) { - if (out.hasArray()) { - System.arraycopy(in, inOffset, out.array(), out.arrayOffset() + outOffset, length); - } else if (UNSAFE_AVAIL) { - UnsafeAccess.copy(in, inOffset, out, outOffset, length); - } else { - final ByteBuffer outDup = out.duplicate(); - outDup.position(outOffset); - outDup.put(in, inOffset, length); - } - } - - /** - * Copies specified number of bytes from given offset of 'in' ByteBuffer to the array. This - * doesn't affect the position of buffer. - * - * @param out output array to copy input bytebuffer to - * @param in input bytebuffer to copy from - * @param sourceOffset offset of source bytebuffer - * @param destinationOffset offset of destination array - * @param length the number of bytes to copy - */ - public static void copyFromBufferToArray(final byte[] out, final ByteBuffer in, final int sourceOffset, - final int destinationOffset, final int length) { - if (in.hasArray()) { - System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out, destinationOffset, length); - } else if (UNSAFE_AVAIL) { - UnsafeAccess.copy(in, sourceOffset, out, destinationOffset, length); - } else { - final ByteBuffer inDup = in.duplicate(); - inDup.position(sourceOffset); - inDup.get(out, destinationOffset, length); - } - } - - /** - * Similar to {@link Arrays#copyOfRange(byte[], int, int)} - * - * @param original the buffer from which the copy has to happen - * @param from the starting index - * @param to the ending index - * @return a byte[] created out of the copy - */ - public static byte[] copyOfRange(final ByteBuffer original, final int from, final int to) { - final int newLength = to - from; - if (newLength < 0) { - throw new IllegalArgumentException(from + " > " + to); - } - final byte[] copy = new byte[newLength]; - ByteBufferUtils.copyFromBufferToArray(copy, original, from, 0, newLength); - return copy; - } - - // For testing purpose - public static String toStringBinary(final ByteBuffer b, final int off, int len) { - final StringBuilder result = new StringBuilder(); - // Just in case we are passed a 'len' that is > buffer length... - if (off >= b.capacity()) { - return result.toString(); - } - if (off + len > b.capacity()) { - len = b.capacity() - off; - } - for (int i = off; i < off + len; ++i) { - final int ch = b.get(i) & 0xFF; - if ( - (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') - || " `~!@#$%^&*()-_=+[]{}|;:'\",.<>/?".indexOf(ch) >= 0 - ) { - result.append((char) ch); - } else { - result.append(String.format("\\x%02X", ch)); - } - } - return result.toString(); - } - - public static String toStringBinary(final ByteBuffer b) { - return toStringBinary(b, 0, b.capacity()); - } - - /** - * Find index of passed delimiter. - * - * @return Index of delimiter having started from start of b moving rightward. - */ - public static int searchDelimiterIndex(final ByteBuffer b, final int offset, final int length, - final int delimiter) { - for (int i = offset, n = offset + length; i < n; i++) { - if (b.get(i) == delimiter) { - return i; - } - } - return -1; - } - - /** - * Find index of passed delimiter walking from end of buffer backwards. - * - * @return Index of delimiter - */ - public static int searchDelimiterIndexInReverse(final ByteBuffer b, final int offset, final int length, - final int delimiter) { - for (int i = offset + length - 1; i >= offset; i--) { - if (b.get(i) == delimiter) { - return i; - } - } - return -1; - } -} diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/Bytes.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/Bytes.java deleted file mode 100644 index 0f7043595a0..00000000000 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/Bytes.java +++ /dev/null @@ -1,2743 +0,0 @@ -/* - * Copy of https://github.com/apache/hbase/blob/master/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java - * to avoid having to pull in all of hbase to use the util methods. - * - * 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 - * - * 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. - */ - -package stroom.bytebuffer.hbase; - -import com.google.common.base.Preconditions; -import org.apache.hadoop.io.RawComparator; -import org.apache.hadoop.io.WritableComparator; -import org.apache.hadoop.io.WritableUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.Random; - - -/** - * Utility class that handles byte arrays, conversions to/from other types, comparisons, hash code - * generation, manufacturing keys for HashMaps or HashSets, and can be used as key in maps or trees. - */ -@SuppressWarnings("MixedMutabilityReturnType") -public class Bytes implements Comparable { - - // Using the charset canonical name for String/byte[] conversions is much - // more efficient due to use of cached encoders/decoders. - private static final String UTF8_CSN = StandardCharsets.UTF_8.name(); - - // HConstants.EMPTY_BYTE_ARRAY should be updated if this changed - private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - - private static final Logger LOG = LoggerFactory.getLogger(Bytes.class); - - /** - * Size of boolean in bytes - */ - public static final int SIZEOF_BOOLEAN = Byte.SIZE / Byte.SIZE; - - /** - * Size of byte in bytes - */ - public static final int SIZEOF_BYTE = SIZEOF_BOOLEAN; - - /** - * Size of char in bytes - */ - public static final int SIZEOF_CHAR = Character.SIZE / Byte.SIZE; - - /** - * Size of double in bytes - */ - public static final int SIZEOF_DOUBLE = Double.SIZE / Byte.SIZE; - - /** - * Size of float in bytes - */ - public static final int SIZEOF_FLOAT = Float.SIZE / Byte.SIZE; - - /** - * Size of int in bytes - */ - public static final int SIZEOF_INT = Integer.SIZE / Byte.SIZE; - - /** - * Size of long in bytes - */ - public static final int SIZEOF_LONG = Long.SIZE / Byte.SIZE; - - /** - * Size of short in bytes - */ - public static final int SIZEOF_SHORT = Short.SIZE / Byte.SIZE; - - /** - * Mask to apply to a long to reveal the lower int only. Use like this: int i = - * (int)(0xFFFFFFFF00000000L ^ some_long_value); - */ - public static final long MASK_FOR_LOWER_INT_IN_LONG = 0xFFFFFFFF00000000L; - - /** - * Estimate of size cost to pay beyond payload in jvm for instance of byte []. Estimate based on - * study of jhat and jprofiler numbers. - */ - // JHat says BU is 56 bytes. - // SizeOf which uses java.lang.instrument says 24 bytes. (3 longs?) - public static final int ESTIMATED_HEAP_TAX = 16; - - static final boolean UNSAFE_UNALIGNED = HBasePlatformDependent.unaligned(); - - /** - * Returns length of the byte array, returning 0 if the array is null. Useful for calculating - * sizes. - * - * @param b byte array, which can be null - * @return 0 if b is null, otherwise returns length - */ - final public static int len(final byte[] b) { - return b == null - ? 0 - : b.length; - } - - private byte[] bytes; - private int offset; - private int length; - - /** - * Create a zero-size sequence. - */ - public Bytes() { - super(); - } - - /** - * Create a Bytes using the byte array as the initial value. - * - * @param bytes This array becomes the backing storage for the object. - */ - public Bytes(final byte[] bytes) { - this(bytes, 0, bytes.length); - } - - /** - * Set the new Bytes to the contents of the passed ibw. - * - * @param ibw the value to set this Bytes to. - */ - public Bytes(final Bytes ibw) { - this(ibw.get(), ibw.getOffset(), ibw.getLength()); - } - - /** - * Set the value to a given byte range - * - * @param bytes the new byte range to set to - * @param offset the offset in newData to start at - * @param length the number of bytes in the range - */ - public Bytes(final byte[] bytes, final int offset, final int length) { - this.bytes = bytes; - this.offset = offset; - this.length = length; - } - - /** - * Get the data from the Bytes. - * - * @return The data is only valid between offset and offset+length. - */ - public byte[] get() { - if (this.bytes == null) { - throw new IllegalStateException( - "Uninitialiized. Null constructor " + "called w/o accompaying readFields invocation"); - } - return this.bytes; - } - - /** - * Use passed bytes as backing array for this instance. - */ - public void set(final byte[] b) { - set(b, 0, b.length); - } - - /** - * Use passed bytes as backing array for this instance. - */ - public void set(final byte[] b, final int offset, final int length) { - this.bytes = b; - this.offset = offset; - this.length = length; - } - - /** - * Returns the number of valid bytes in the buffer - */ - public int getLength() { - if (this.bytes == null) { - throw new IllegalStateException( - "Uninitialiized. Null constructor " + "called w/o accompaying readFields invocation"); - } - return this.length; - } - - /** - * Return the offset into the buffer. - */ - public int getOffset() { - return this.offset; - } - - @Override - public int hashCode() { - return Bytes.hashCode(bytes, offset, length); - } - - /** - * Define the sort order of the Bytes. - * - * @param that The other bytes writable - * @return Positive if left is bigger than right, 0 if they are equal, and negative if left is - * smaller than right. - */ - @Override - public int compareTo(final Bytes that) { - return BYTES_RAWCOMPARATOR.compare(this.bytes, this.offset, this.length, that.bytes, - that.offset, that.length); - } - - /** - * Compares the bytes in this object to the specified byte array - * - * @return Positive if left is bigger than right, 0 if they are equal, and negative if left is - * smaller than right. - */ - public int compareTo(final byte[] that) { - return BYTES_RAWCOMPARATOR.compare(this.bytes, this.offset, this.length, that, 0, that.length); - } - - @Override - public boolean equals(final Object right_obj) { - if (right_obj instanceof byte[]) { - return compareTo((byte[]) right_obj) == 0; - } - if (right_obj instanceof Bytes) { - return compareTo((Bytes) right_obj) == 0; - } - return false; - } - - @Override - public String toString() { - return Bytes.toString(bytes, offset, length); - } - - /** - * Convert a list of byte[] to an array - * - * @param array List of byte []. - * @return Array of byte []. - */ - public static byte[][] toArray(final List array) { - // List#toArray doesn't work on lists of byte []. - final byte[][] results = new byte[array.size()][]; - for (int i = 0; i < array.size(); i++) { - results[i] = array.get(i); - } - return results; - } - - /** - * Returns a copy of the bytes referred to by this writable - */ - public byte[] copyBytes() { - return Arrays.copyOfRange(bytes, offset, offset + length); - } - - /** - * Byte array comparator class. - */ - public static class ByteArrayComparator implements RawComparator { - - public ByteArrayComparator() { - super(); - } - - @Override - public int compare(final byte[] left, final byte[] right) { - return compareTo(left, right); - } - - @Override - public int compare(final byte[] b1, final int s1, final int l1, final byte[] b2, final int s2, final int l2) { - return LexicographicalComparerHolder.BEST_COMPARER.compareTo(b1, s1, l1, b2, s2, l2); - } - } - - /** - * A {@link ByteArrayComparator} that treats the empty array as the largest value. This is useful - * for comparing row end keys for regions. - */ - // TODO: unfortunately, HBase uses byte[0] as both start and end keys for region - // boundaries. Thus semantically, we should treat empty byte array as the smallest value - // while comparing row keys, start keys etc; but as the largest value for comparing - // region boundaries for endKeys. - public static class RowEndKeyComparator extends ByteArrayComparator { - - @Override - public int compare(final byte[] left, final byte[] right) { - return compare(left, 0, left.length, right, 0, right.length); - } - - @Override - public int compare(final byte[] b1, final int s1, final int l1, final byte[] b2, final int s2, final int l2) { - if (b1 == b2 && s1 == s2 && l1 == l2) { - return 0; - } - if (l1 == 0) { - return l2; // 0 or positive - } - if (l2 == 0) { - return -1; - } - return super.compare(b1, s1, l1, b2, s2, l2); - } - } - - /** - * Pass this to TreeMaps where byte [] are keys. - */ - public final static Comparator BYTES_COMPARATOR = new ByteArrayComparator(); - - /** - * Use comparing byte arrays, byte-by-byte - */ - public final static RawComparator BYTES_RAWCOMPARATOR = new ByteArrayComparator(); - - /** - * Read byte-array written with a WritableableUtils.vint prefix. - * - * @param in Input to read from. - * @return byte array read off in - * @throws IOException e - */ - public static byte[] readByteArray(final DataInput in) throws IOException { - final int len = WritableUtils.readVInt(in); - if (len < 0) { - throw new NegativeArraySizeException(Integer.toString(len)); - } - final byte[] result = new byte[len]; - in.readFully(result, 0, len); - return result; - } - - /** - * Read byte-array written with a WritableableUtils.vint prefix. IOException is converted to a - * RuntimeException. - * - * @param in Input to read from. - * @return byte array read off in - */ - public static byte[] readByteArrayThrowsRuntime(final DataInput in) { - try { - return readByteArray(in); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Write byte-array with a WritableableUtils.vint prefix. - * - * @param out output stream to be written to - * @param b array to write - * @throws IOException e - */ - public static void writeByteArray(final DataOutput out, final byte[] b) throws IOException { - if (b == null) { - WritableUtils.writeVInt(out, 0); - } else { - writeByteArray(out, b, 0, b.length); - } - } - - /** - * Write byte-array to out with a vint length prefix. - * - * @param out output stream - * @param b array - * @param offset offset into array - * @param length length past offset - * @throws IOException e - */ - public static void writeByteArray(final DataOutput out, final byte[] b, final int offset, - final int length) throws IOException { - WritableUtils.writeVInt(out, length); - out.write(b, offset, length); - } - - /** - * Write byte-array from src to tgt with a vint length prefix. - * - * @param tgt target array - * @param tgtOffset offset into target array - * @param src source array - * @param srcOffset source offset - * @param srcLength source length - * @return New offset in src array. - */ - public static int writeByteArray(final byte[] tgt, final int tgtOffset, final byte[] src, - final int srcOffset, final int srcLength) { - final byte[] vint = vintToBytes(srcLength); - System.arraycopy(vint, 0, tgt, tgtOffset, vint.length); - final int offset = tgtOffset + vint.length; - System.arraycopy(src, srcOffset, tgt, offset, srcLength); - return offset + srcLength; - } - - /** - * Put bytes at the specified byte array position. - * - * @param tgtBytes the byte array - * @param tgtOffset position in the array - * @param srcBytes array to write out - * @param srcOffset source offset - * @param srcLength source length - * @return incremented offset - */ - public static int putBytes(final byte[] tgtBytes, final int tgtOffset, final byte[] srcBytes, final int srcOffset, - final int srcLength) { - System.arraycopy(srcBytes, srcOffset, tgtBytes, tgtOffset, srcLength); - return tgtOffset + srcLength; - } - - /** - * Write a single byte out to the specified byte array position. - * - * @param bytes the byte array - * @param offset position in the array - * @param b byte to write out - * @return incremented offset - */ - public static int putByte(final byte[] bytes, final int offset, final byte b) { - bytes[offset] = b; - return offset + 1; - } - - /** - * Add the whole content of the ByteBuffer to the bytes arrays. The ByteBuffer is modified. - * - * @param bytes the byte array - * @param offset position in the array - * @param buf ByteBuffer to write out - * @return incremented offset - */ - public static int putByteBuffer(final byte[] bytes, final int offset, final ByteBuffer buf) { - final int len = buf.remaining(); - buf.get(bytes, offset, len); - return offset + len; - } - - /** - * Returns a new byte array, copied from the given {@code buf}, from the index 0 (inclusive) to - * the limit (exclusive), regardless of the current position. The position and the other index - * parameters are not changed. - * - * @param buf a byte buffer - * @return the byte array - * @see #getBytes(ByteBuffer) - */ - public static byte[] toBytes(final ByteBuffer buf) { - final ByteBuffer dup = buf.duplicate(); - dup.position(0); - return readBytes(dup); - } - - private static byte[] readBytes(final ByteBuffer buf) { - final byte[] result = new byte[buf.remaining()]; - buf.get(result); - return result; - } - - /** - * Convert a byte[] into a string. Charset is assumed to be UTF-8. - * - * @param b Presumed UTF-8 encoded byte array. - * @return String made from b - */ - public static String toString(final byte[] b) { - if (b == null) { - return null; - } - return toString(b, 0, b.length); - } - - /** - * Joins two byte arrays together using a separator. - * - * @param b1 The first byte array. - * @param sep The separator to use. - * @param b2 The second byte array. - */ - public static String toString(final byte[] b1, final String sep, final byte[] b2) { - return toString(b1, 0, b1.length) + sep + toString(b2, 0, b2.length); - } - - /** - * This method will convert utf8 encoded bytes into a string. If the given byte array is null, - * this method will return null. - * - * @param b Presumed UTF-8 encoded byte array. - * @param off offset into array - * @return String made from b or null - */ - public static String toString(final byte[] b, final int off) { - if (b == null) { - return null; - } - final int len = b.length - off; - if (len <= 0) { - return ""; - } - try { - return new String(b, off, len, UTF8_CSN); - } catch (final UnsupportedEncodingException e) { - // should never happen! - throw new IllegalArgumentException("UTF8 encoding is not supported", e); - } - } - - /** - * This method will convert utf8 encoded bytes into a string. If the given byte array is null, - * this method will return null. - * - * @param b Presumed UTF-8 encoded byte array. - * @param off offset into array - * @param len length of utf-8 sequence - * @return String made from b or null - */ - public static String toString(final byte[] b, final int off, final int len) { - if (b == null) { - return null; - } - if (len == 0) { - return ""; - } - try { - return new String(b, off, len, UTF8_CSN); - } catch (final UnsupportedEncodingException e) { - // should never happen! - throw new IllegalArgumentException("UTF8 encoding is not supported", e); - } - } - - /** - * Write a printable representation of a byte array. - * - * @param b byte array - * @see #toStringBinary(byte[], int, int) - */ - public static String toStringBinary(final byte[] b) { - if (b == null) { - return "null"; - } - return toStringBinary(b, 0, b.length); - } - - /** - * Converts the given byte buffer to a printable representation, from the index 0 (inclusive) to - * the limit (exclusive), regardless of the current position. The position and the other index - * parameters are not changed. - * - * @param buf a byte buffer - * @return a string representation of the buffer's binary contents - * @see #toBytes(ByteBuffer) - * @see #getBytes(ByteBuffer) - */ - public static String toStringBinary(final ByteBuffer buf) { - if (buf == null) { - return "null"; - } - if (buf.hasArray()) { - return toStringBinary(buf.array(), buf.arrayOffset(), buf.limit()); - } - return toStringBinary(toBytes(buf)); - } - - private static final char[] HEX_CHARS_UPPER = - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - /** - * Write a printable representation of a byte array. Non-printable characters are hex escaped in - * the format \\x%02X, eg: \x00 \x05 etc - * - * @param b array to write out - * @param off offset to start at - * @param len length to write - * @return string output - */ - public static String toStringBinary(final byte[] b, final int off, int len) { - final StringBuilder result = new StringBuilder(); - // Just in case we are passed a 'len' that is > buffer length... - if (off >= b.length) { - return result.toString(); - } - if (off + len > b.length) { - len = b.length - off; - } - for (int i = off; i < off + len; ++i) { - final int ch = b[i] & 0xFF; - if (ch >= ' ' && ch <= '~' && ch != '\\') { - result.append((char) ch); - } else { - result.append("\\x"); - result.append(HEX_CHARS_UPPER[ch / 0x10]); - result.append(HEX_CHARS_UPPER[ch % 0x10]); - } - } - return result.toString(); - } - - private static boolean isHexDigit(final char c) { - return (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'); - } - - /** - * Takes a ASCII digit in the range A-F0-9 and returns the corresponding integer/ordinal value. - * - * @param ch The hex digit. - * @return The converted hex value as a byte. - */ - public static byte toBinaryFromHex(final byte ch) { - if (ch >= 'A' && ch <= 'F') { - return (byte) ((byte) 10 + (byte) (ch - 'A')); - } - // else - return (byte) (ch - '0'); - } - - public static byte[] toBytesBinary(final String in) { - // this may be bigger than we need, but let's be safe. - final byte[] b = new byte[in.length()]; - int size = 0; - for (int i = 0; i < in.length(); ++i) { - final char ch = in.charAt(i); - if (ch == '\\' && in.length() > i + 1 && in.charAt(i + 1) == 'x') { - // ok, take next 2 hex digits. - final char hd1 = in.charAt(i + 2); - final char hd2 = in.charAt(i + 3); - - // they need to be A-F0-9: - if (!isHexDigit(hd1) || !isHexDigit(hd2)) { - // bogus escape code, ignore: - continue; - } - // turn hex ASCII digit -> number - final byte d = (byte) ((toBinaryFromHex((byte) hd1) << 4) + toBinaryFromHex((byte) hd2)); - - b[size++] = d; - i += 3; // skip 3 - } else { - b[size++] = (byte) ch; - } - } - // resize: - final byte[] b2 = new byte[size]; - System.arraycopy(b, 0, b2, 0, size); - return b2; - } - - /** - * Converts a string to a UTF-8 byte array. - * - * @param s string - * @return the byte array - */ - public static byte[] toBytes(final String s) { - try { - return s.getBytes(UTF8_CSN); - } catch (final UnsupportedEncodingException e) { - // should never happen! - throw new IllegalArgumentException("UTF8 decoding is not supported", e); - } - } - - /** - * Convert a boolean to a byte array. True becomes -1 and false becomes 0. - * - * @param b value - * @return b encoded in a byte array. - */ - public static byte[] toBytes(final boolean b) { - return new byte[]{ - b - ? (byte) -1 - : (byte) 0}; - } - - /** - * Reverses {@link #toBytes(boolean)} - * - * @param b array - * @return True or false. - */ - public static boolean toBoolean(final byte[] b) { - if (b.length != 1) { - throw new IllegalArgumentException("Array has wrong size: " + b.length); - } - return b[0] != (byte) 0; - } - - /** - * Convert a long value to a byte array using big-endian. - * - * @param val value to convert - * @return the byte array - */ - public static byte[] toBytes(long val) { - final byte[] b = new byte[8]; - for (int i = 7; i > 0; i--) { - b[i] = (byte) val; - val >>>= 8; - } - b[0] = (byte) val; - return b; - } - - /** - * Converts a byte array to a long value. Reverses {@link #toBytes(long)} - * - * @param bytes array - * @return the long value - */ - public static long toLong(final byte[] bytes) { - return toLong(bytes, 0, SIZEOF_LONG); - } - - /** - * Converts a byte array to a long value. Assumes there will be {@link #SIZEOF_LONG} bytes - * available. - * - * @param bytes bytes - * @param offset offset - * @return the long value - */ - public static long toLong(final byte[] bytes, final int offset) { - return toLong(bytes, offset, SIZEOF_LONG); - } - - /** - * Converts a byte array to a long value. - * - * @param bytes array of bytes - * @param offset offset into array - * @param length length of data (must be {@link #SIZEOF_LONG}) - * @return the long value - * @throws IllegalArgumentException if length is not {@link #SIZEOF_LONG} or if there's not enough - * room in the array at the offset indicated. - */ - public static long toLong(final byte[] bytes, final int offset, final int length) { - if (length != SIZEOF_LONG || offset + length > bytes.length) { - throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_LONG); - } - return ConverterHolder.BEST_CONVERTER.toLong(bytes, offset, length); - } - - private static IllegalArgumentException explainWrongLengthOrOffset(final byte[] bytes, - final int offset, - final int length, - final int expectedLength) { - final String reason; - if (length != expectedLength) { - reason = "Wrong length: " + length + ", expected " + expectedLength; - } else { - reason = "offset (" + offset + ") + length (" + length + ") exceed the" - + " capacity of the array: " + bytes.length; - } - return new IllegalArgumentException(reason); - } - - /** - * Put a long value out to the specified byte array position. - * - * @param bytes the byte array - * @param offset position in the array - * @param val long to write out - * @return incremented offset - * @throws IllegalArgumentException if the byte array given doesn't have enough room at the offset - * specified. - */ - public static int putLong(final byte[] bytes, final int offset, final long val) { - if (bytes.length - offset < SIZEOF_LONG) { - throw new IllegalArgumentException("Not enough room to put a long at" + " offset " + offset - + " in a " + bytes.length + " byte array"); - } - return ConverterHolder.BEST_CONVERTER.putLong(bytes, offset, val); - } - - /** - * Put a float value out to the specified byte array position. Presumes float encoded as IEEE 754 - * floating-point "single format" - * - * @param bytes byte array - * @return Float made from passed byte array. - */ - public static float toFloat(final byte[] bytes) { - return toFloat(bytes, 0); - } - - /** - * Put a float value out to the specified byte array position. Presumes float encoded as IEEE 754 - * floating-point "single format" - * - * @param bytes array to convert - * @param offset offset into array - * @return Float made from passed byte array. - */ - public static float toFloat(final byte[] bytes, final int offset) { - return Float.intBitsToFloat(toInt(bytes, offset, SIZEOF_INT)); - } - - /** - * Put a float value out to the specified byte array position. - * - * @param bytes byte array - * @param offset offset to write to - * @param f float value - * @return New offset in bytes - */ - public static int putFloat(final byte[] bytes, final int offset, final float f) { - return putInt(bytes, offset, Float.floatToRawIntBits(f)); - } - - /** - * Return the float represented as byte[] - */ - public static byte[] toBytes(final float f) { - // Encode it as int - return Bytes.toBytes(Float.floatToRawIntBits(f)); - } - - /** - * Return double made from passed bytes. - */ - public static double toDouble(final byte[] bytes) { - return toDouble(bytes, 0); - } - - /** - * Return double made from passed bytes. - */ - public static double toDouble(final byte[] bytes, final int offset) { - return Double.longBitsToDouble(toLong(bytes, offset, SIZEOF_LONG)); - } - - /** - * Put a double value out to the specified byte array position as the IEEE 754 double format. - * - * @param bytes byte array - * @param offset offset to write to - * @param d value - * @return New offset into array bytes - */ - public static int putDouble(final byte[] bytes, final int offset, final double d) { - return putLong(bytes, offset, Double.doubleToLongBits(d)); - } - - /** - * Serialize a double as the IEEE 754 double format output. The resultant array will be 8 bytes - * long. - * - * @param d value - * @return the double represented as byte [] - */ - public static byte[] toBytes(final double d) { - // Encode it as a long - return Bytes.toBytes(Double.doubleToRawLongBits(d)); - } - - /** - * Convert an int value to a byte array. Big-endian. Same as what DataOutputStream.writeInt does. - * - * @param val value - * @return the byte array - */ - public static byte[] toBytes(int val) { - final byte[] b = new byte[4]; - for (int i = 3; i > 0; i--) { - b[i] = (byte) val; - val >>>= 8; - } - b[0] = (byte) val; - return b; - } - - /** - * Converts a byte array to an int value - * - * @param bytes byte array - * @return the int value - */ - public static int toInt(final byte[] bytes) { - return toInt(bytes, 0, SIZEOF_INT); - } - - /** - * Converts a byte array to an int value - * - * @param bytes byte array - * @param offset offset into array - * @return the int value - */ - public static int toInt(final byte[] bytes, final int offset) { - return toInt(bytes, offset, SIZEOF_INT); - } - - /** - * Converts a byte array to an int value - * - * @param bytes byte array - * @param offset offset into array - * @param length length of int (has to be {@link #SIZEOF_INT}) - * @return the int value - * @throws IllegalArgumentException if length is not {@link #SIZEOF_INT} or if there's not enough - * room in the array at the offset indicated. - */ - public static int toInt(final byte[] bytes, final int offset, final int length) { - if (length != SIZEOF_INT || offset + length > bytes.length) { - throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_INT); - } - return ConverterHolder.BEST_CONVERTER.toInt(bytes, offset, length); - } - - /** - * Converts a byte array to an int value - * - * @param bytes byte array - * @param offset offset into array - * @param length how many bytes should be considered for creating int - * @return the int value - * @throws IllegalArgumentException if there's not enough room in the array at the offset - * indicated. - */ - public static int readAsInt(final byte[] bytes, final int offset, final int length) { - if (offset + length > bytes.length) { - throw new IllegalArgumentException("offset (" + offset + ") + length (" + length - + ") exceed the" + " capacity of the array: " + bytes.length); - } - int n = 0; - for (int i = offset; i < (offset + length); i++) { - n <<= 8; - n ^= bytes[i] & 0xFF; - } - return n; - } - - /** - * Put an int value out to the specified byte array position. - * - * @param bytes the byte array - * @param offset position in the array - * @param val int to write out - * @return incremented offset - * @throws IllegalArgumentException if the byte array given doesn't have enough room at the offset - * specified. - */ - public static int putInt(final byte[] bytes, final int offset, final int val) { - if (bytes.length - offset < SIZEOF_INT) { - throw new IllegalArgumentException("Not enough room to put an int at" + " offset " + offset - + " in a " + bytes.length + " byte array"); - } - return ConverterHolder.BEST_CONVERTER.putInt(bytes, offset, val); - } - - /** - * Convert a short value to a byte array of {@link #SIZEOF_SHORT} bytes long. - * - * @param val value - * @return the byte array - */ - public static byte[] toBytes(short val) { - final byte[] b = new byte[SIZEOF_SHORT]; - b[1] = (byte) val; - val >>= 8; - b[0] = (byte) val; - return b; - } - - /** - * Converts a byte array to a short value - * - * @param bytes byte array - * @return the short value - */ - public static short toShort(final byte[] bytes) { - return toShort(bytes, 0, SIZEOF_SHORT); - } - - /** - * Converts a byte array to a short value - * - * @param bytes byte array - * @param offset offset into array - * @return the short value - */ - public static short toShort(final byte[] bytes, final int offset) { - return toShort(bytes, offset, SIZEOF_SHORT); - } - - /** - * Converts a byte array to a short value - * - * @param bytes byte array - * @param offset offset into array - * @param length length, has to be {@link #SIZEOF_SHORT} - * @return the short value - * @throws IllegalArgumentException if length is not {@link #SIZEOF_SHORT} or if there's not - * enough room in the array at the offset indicated. - */ - public static short toShort(final byte[] bytes, final int offset, final int length) { - if (length != SIZEOF_SHORT || offset + length > bytes.length) { - throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_SHORT); - } - return ConverterHolder.BEST_CONVERTER.toShort(bytes, offset, length); - } - - /** - * Returns a new byte array, copied from the given {@code buf}, from the position (inclusive) to - * the limit (exclusive). The position and the other index parameters are not changed. - * - * @param buf a byte buffer - * @return the byte array - * @see #toBytes(ByteBuffer) - */ - public static byte[] getBytes(final ByteBuffer buf) { - return readBytes(buf.duplicate()); - } - - /** - * Put a short value out to the specified byte array position. - * - * @param bytes the byte array - * @param offset position in the array - * @param val short to write out - * @return incremented offset - * @throws IllegalArgumentException if the byte array given doesn't have enough room at the offset - * specified. - */ - public static int putShort(final byte[] bytes, final int offset, final short val) { - if (bytes.length - offset < SIZEOF_SHORT) { - throw new IllegalArgumentException("Not enough room to put a short at" + " offset " + offset - + " in a " + bytes.length + " byte array"); - } - return ConverterHolder.BEST_CONVERTER.putShort(bytes, offset, val); - } - - /** - * Put an int value as short out to the specified byte array position. Only the lower 2 bytes of - * the short will be put into the array. The caller of the API need to make sure they will not - * loose the value by doing so. This is useful to store an unsigned short which is represented as - * int in other parts. - * - * @param bytes the byte array - * @param offset position in the array - * @param val value to write out - * @return incremented offset - * @throws IllegalArgumentException if the byte array given doesn't have enough room at the offset - * specified. - */ - public static int putAsShort(final byte[] bytes, final int offset, int val) { - if (bytes.length - offset < SIZEOF_SHORT) { - throw new IllegalArgumentException("Not enough room to put a short at" + " offset " + offset - + " in a " + bytes.length + " byte array"); - } - bytes[offset + 1] = (byte) val; - val >>= 8; - bytes[offset] = (byte) val; - return offset + SIZEOF_SHORT; - } - - /** - * Convert a BigDecimal value to a byte array - */ - public static byte[] toBytes(final BigDecimal val) { - final byte[] valueBytes = val.unscaledValue().toByteArray(); - final byte[] result = new byte[valueBytes.length + SIZEOF_INT]; - final int offset = putInt(result, 0, val.scale()); - putBytes(result, offset, valueBytes, 0, valueBytes.length); - return result; - } - - /** - * Converts a byte array to a BigDecimal - */ - public static BigDecimal toBigDecimal(final byte[] bytes) { - return toBigDecimal(bytes, 0, bytes.length); - } - - /** - * Converts a byte array to a BigDecimal value - */ - public static BigDecimal toBigDecimal(final byte[] bytes, final int offset, final int length) { - if (bytes == null || length < SIZEOF_INT + 1 || (offset + length > bytes.length)) { - return null; - } - - final int scale = toInt(bytes, offset); - final byte[] tcBytes = new byte[length - SIZEOF_INT]; - System.arraycopy(bytes, offset + SIZEOF_INT, tcBytes, 0, length - SIZEOF_INT); - return new BigDecimal(new BigInteger(tcBytes), scale); - } - - /** - * Put a BigDecimal value out to the specified byte array position. - * - * @param bytes the byte array - * @param offset position in the array - * @param val BigDecimal to write out - * @return incremented offset - */ - public static int putBigDecimal(final byte[] bytes, int offset, final BigDecimal val) { - if (bytes == null) { - return offset; - } - - final byte[] valueBytes = val.unscaledValue().toByteArray(); - final byte[] result = new byte[valueBytes.length + SIZEOF_INT]; - offset = putInt(result, offset, val.scale()); - return putBytes(result, offset, valueBytes, 0, valueBytes.length); - } - - /** - * Encode a long value as a variable length integer. - * - * @param vint Integer to make a vint of. - * @return Vint as bytes array. - */ - public static byte[] vintToBytes(final long vint) { - long i = vint; - final int size = WritableUtils.getVIntSize(i); - final byte[] result = new byte[size]; - int offset = 0; - if (i >= -112 && i <= 127) { - result[offset] = (byte) i; - return result; - } - - int len = -112; - if (i < 0) { - i ^= -1L; // take one's complement' - len = -120; - } - - long tmp = i; - while (tmp != 0) { - tmp = tmp >> 8; - len--; - } - - result[offset++] = (byte) len; - - len = (len < -120) - ? -(len + 120) - : -(len + 112); - - for (int idx = len; idx != 0; idx--) { - final int shiftbits = (idx - 1) * 8; - final long mask = 0xFFL << shiftbits; - result[offset++] = (byte) ((i & mask) >> shiftbits); - } - return result; - } - - /** - * Reads a zero-compressed encoded long from input buffer and returns it. - * - * @param buffer buffer to convert - * @return vint bytes as an integer. - */ - public static long bytesToVint(final byte[] buffer) { - int offset = 0; - final byte firstByte = buffer[offset++]; - final int len = WritableUtils.decodeVIntSize(firstByte); - if (len == 1) { - return firstByte; - } - long i = 0; - for (int idx = 0; idx < len - 1; idx++) { - final byte b = buffer[offset++]; - i = i << 8; - i = i | (b & 0xFF); - } - return (WritableUtils.isNegativeVInt(firstByte) - ? ~i - : i); - } - - /** - * Reads a zero-compressed encoded long from input buffer and returns it. - * - * @param buffer Binary array - * @param offset Offset into array at which vint begins. - * @return deserialized long from buffer. - */ - public static long readAsVLong(final byte[] buffer, final int offset) { - final byte firstByte = buffer[offset]; - final int len = WritableUtils.decodeVIntSize(firstByte); - if (len == 1) { - return firstByte; - } - long i = 0; - for (int idx = 0; idx < len - 1; idx++) { - final byte b = buffer[offset + 1 + idx]; - i = i << 8; - i = i | (b & 0xFF); - } - return (WritableUtils.isNegativeVInt(firstByte) - ? ~i - : i); - } - - /** - * Lexicographically compare two arrays. - * - * @param left left operand - * @param right right operand - * @return 0 if equal, < 0 if left is less than right, etc. - */ - public static int compareTo(final byte[] left, final byte[] right) { - return LexicographicalComparerHolder.BEST_COMPARER.compareTo(left, - 0, - left == null - ? 0 - : left.length, - right, - 0, - right == null - ? 0 - : right.length); - } - - /** - * Lexicographically compare two arrays. - * - * @param buffer1 left operand - * @param buffer2 right operand - * @param offset1 Where to start comparing in the left buffer - * @param offset2 Where to start comparing in the right buffer - * @param length1 How much to compare from the left buffer - * @param length2 How much to compare from the right buffer - * @return 0 if equal, < 0 if left is less than right, etc. - */ - public static int compareTo(final byte[] buffer1, final int offset1, final int length1, final byte[] buffer2, final int offset2, - final int length2) { - return LexicographicalComparerHolder.BEST_COMPARER.compareTo(buffer1, offset1, length1, buffer2, - offset2, length2); - } - - interface Comparer { - - int compareTo(T buffer1, int offset1, int length1, T buffer2, int offset2, int length2); - } - - static abstract class Converter { - - abstract long toLong(byte[] bytes, int offset, int length); - - abstract int putLong(byte[] bytes, int offset, long val); - - abstract int toInt(byte[] bytes, int offset, final int length); - - abstract int putInt(byte[] bytes, int offset, int val); - - abstract short toShort(byte[] bytes, int offset, final int length); - - abstract int putShort(byte[] bytes, int offset, short val); - - } - - static abstract class CommonPrefixer { - - abstract int findCommonPrefix(byte[] left, int leftOffset, int leftLength, byte[] right, - int rightOffset, int rightLength); - } - - static Comparer lexicographicalComparerJavaImpl() { - return LexicographicalComparerHolder.PureJavaComparer.INSTANCE; - } - - static class ConverterHolder { - - static final String UNSAFE_CONVERTER_NAME = - ConverterHolder.class.getName() + "$UnsafeConverter"; - - static final Converter BEST_CONVERTER = getBestConverter(); - - /** - * Returns the Unsafe-using Converter, or falls back to the pure-Java implementation if unable - * to do so. - */ - static Converter getBestConverter() { - try { - final Class theClass = Class.forName(UNSAFE_CONVERTER_NAME); - - // yes, UnsafeComparer does implement Comparer - @SuppressWarnings("unchecked") final Converter converter = (Converter) theClass.getConstructor().newInstance(); - return converter; - } catch (final Throwable t) { // ensure we really catch *everything* - return PureJavaConverter.INSTANCE; - } - } - - protected static final class PureJavaConverter extends Converter { - - static final PureJavaConverter INSTANCE = new PureJavaConverter(); - - private PureJavaConverter() { - } - - @Override - long toLong(final byte[] bytes, final int offset, final int length) { - long l = 0; - for (int i = offset; i < offset + length; i++) { - l <<= 8; - l ^= bytes[i] & 0xFF; - } - return l; - } - - @Override - int putLong(final byte[] bytes, final int offset, long val) { - for (int i = offset + 7; i > offset; i--) { - bytes[i] = (byte) val; - val >>>= 8; - } - bytes[offset] = (byte) val; - return offset + SIZEOF_LONG; - } - - @Override - int toInt(final byte[] bytes, final int offset, final int length) { - int n = 0; - for (int i = offset; i < (offset + length); i++) { - n <<= 8; - n ^= bytes[i] & 0xFF; - } - return n; - } - - @Override - int putInt(final byte[] bytes, final int offset, int val) { - for (int i = offset + 3; i > offset; i--) { - bytes[i] = (byte) val; - val >>>= 8; - } - bytes[offset] = (byte) val; - return offset + SIZEOF_INT; - } - - @Override - short toShort(final byte[] bytes, final int offset, final int length) { - short n = 0; - n = (short) ((n ^ bytes[offset]) & 0xFF); - n = (short) (n << 8); - n ^= (short) (bytes[offset + 1] & 0xFF); - return n; - } - - @Override - int putShort(final byte[] bytes, final int offset, short val) { - bytes[offset + 1] = (byte) val; - val >>= 8; - bytes[offset] = (byte) val; - return offset + SIZEOF_SHORT; - } - } - - protected static final class UnsafeConverter extends Converter { - - public UnsafeConverter() { - } - - static { - if (!UNSAFE_UNALIGNED) { - // It doesn't matter what we throw; - // it's swallowed in getBestComparer(). - throw new Error(); - } - - // sanity check - this should never fail - if (HBasePlatformDependent.arrayIndexScale(byte[].class) != 1) { - throw new AssertionError(); - } - } - - @Override - long toLong(final byte[] bytes, final int offset, final int length) { - return UnsafeAccess.toLong(bytes, offset); - } - - @Override - int putLong(final byte[] bytes, final int offset, final long val) { - return UnsafeAccess.putLong(bytes, offset, val); - } - - @Override - int toInt(final byte[] bytes, final int offset, final int length) { - return UnsafeAccess.toInt(bytes, offset); - } - - @Override - int putInt(final byte[] bytes, final int offset, final int val) { - return UnsafeAccess.putInt(bytes, offset, val); - } - - @Override - short toShort(final byte[] bytes, final int offset, final int length) { - return UnsafeAccess.toShort(bytes, offset); - } - - @Override - int putShort(final byte[] bytes, final int offset, final short val) { - return UnsafeAccess.putShort(bytes, offset, val); - } - } - } - - /** - * Provides a lexicographical comparer implementation; either a Java implementation or a faster - * implementation based on {@code Unsafe}. - *

- * Uses reflection to gracefully fall back to the Java implementation if {@code Unsafe} isn't - * available. - */ - static class LexicographicalComparerHolder { - - static final String UNSAFE_COMPARER_NAME = - LexicographicalComparerHolder.class.getName() + "$UnsafeComparer"; - - static final Comparer BEST_COMPARER = getBestComparer(); - - /** - * Returns the Unsafe-using Comparer, or falls back to the pure-Java implementation if unable to - * do so. - */ - static Comparer getBestComparer() { - try { - final Class theClass = Class.forName(UNSAFE_COMPARER_NAME); - - // yes, UnsafeComparer does implement Comparer - @SuppressWarnings("unchecked") final Comparer comparer = (Comparer) theClass.getEnumConstants()[0]; - return comparer; - } catch (final Throwable t) { // ensure we really catch *everything* - return lexicographicalComparerJavaImpl(); - } - } - - enum PureJavaComparer implements Comparer { - INSTANCE; - - @Override - public int compareTo(final byte[] buffer1, final int offset1, final int length1, final byte[] buffer2, final int offset2, - final int length2) { - // Short circuit equal case - if (buffer1 == buffer2 && offset1 == offset2 && length1 == length2) { - return 0; - } - // Bring WritableComparator code local - final int end1 = offset1 + length1; - final int end2 = offset2 + length2; - for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { - final int a = (buffer1[i] & 0xff); - final int b = (buffer2[j] & 0xff); - if (a != b) { - return a - b; - } - } - return length1 - length2; - } - } - - enum UnsafeComparer implements Comparer { - INSTANCE; - - static { - if (!UNSAFE_UNALIGNED) { - // It doesn't matter what we throw; - // it's swallowed in getBestComparer(). - throw new Error(); - } - - // sanity check - this should never fail - if (HBasePlatformDependent.arrayIndexScale(byte[].class) != 1) { - throw new AssertionError(); - } - } - - /** - * Lexicographically compare two arrays. - * - * @param buffer1 left operand - * @param buffer2 right operand - * @param offset1 Where to start comparing in the left buffer - * @param offset2 Where to start comparing in the right buffer - * @param length1 How much to compare from the left buffer - * @param length2 How much to compare from the right buffer - * @return 0 if equal, < 0 if left is less than right, etc. - */ - @Override - public int compareTo(final byte[] buffer1, final int offset1, final int length1, final byte[] buffer2, final int offset2, - final int length2) { - - // Short circuit equal case - if (buffer1 == buffer2 && offset1 == offset2 && length1 == length2) { - return 0; - } - final int stride = 8; - final int minLength = Math.min(length1, length2); - final int strideLimit = minLength & ~(stride - 1); - final long offset1Adj = offset1 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - final long offset2Adj = offset2 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - int i; - - /* - * Compare 8 bytes at a time. Benchmarking on x86 shows a stride of 8 bytes is no slower - * than 4 bytes even on 32-bit. On the other hand, it is substantially faster on 64-bit. - */ - for (i = 0; i < strideLimit; i += stride) { - final long lw = HBasePlatformDependent.getLong(buffer1, offset1Adj + i); - final long rw = HBasePlatformDependent.getLong(buffer2, offset2Adj + i); - if (lw != rw) { - if (!UnsafeAccess.LITTLE_ENDIAN) { - return ((lw + Long.MIN_VALUE) < (rw + Long.MIN_VALUE)) - ? -1 - : 1; - } - - /* - * We want to compare only the first index where left[index] != right[index]. This - * corresponds to the least significant nonzero byte in lw ^ rw, since lw and rw are - * little-endian. Long.numberOfTrailingZeros(diff) tells us the least significant - * nonzero bit, and zeroing out the first three bits of L.nTZ gives us the shift to get - * that least significant nonzero byte. This comparison logic is based on UnsignedBytes - * comparator from guava v21 - */ - final int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; - return ((int) ((lw >>> n) & 0xFF)) - ((int) ((rw >>> n) & 0xFF)); - } - } - - // The epilogue to cover the last (minLength % stride) elements. - for (; i < minLength; i++) { - final int a = (buffer1[offset1 + i] & 0xFF); - final int b = (buffer2[offset2 + i] & 0xFF); - if (a != b) { - return a - b; - } - } - return length1 - length2; - } - } - } - - static class CommonPrefixerHolder { - - static final String UNSAFE_COMMON_PREFIXER_NAME = - CommonPrefixerHolder.class.getName() + "$UnsafeCommonPrefixer"; - - static final CommonPrefixer BEST_COMMON_PREFIXER = getBestCommonPrefixer(); - - static CommonPrefixer getBestCommonPrefixer() { - try { - final Class theClass = - Class.forName(UNSAFE_COMMON_PREFIXER_NAME).asSubclass(CommonPrefixer.class); - - return theClass.getConstructor().newInstance(); - } catch (final Throwable t) { // ensure we really catch *everything* - return CommonPrefixerHolder.PureJavaCommonPrefixer.INSTANCE; - } - } - - static final class PureJavaCommonPrefixer extends CommonPrefixer { - - static final PureJavaCommonPrefixer INSTANCE = new PureJavaCommonPrefixer(); - - private PureJavaCommonPrefixer() { - } - - @Override - public int findCommonPrefix(final byte[] left, final int leftOffset, final int leftLength, final byte[] right, - final int rightOffset, final int rightLength) { - final int length = Math.min(leftLength, rightLength); - int result = 0; - - while (result < length && left[leftOffset + result] == right[rightOffset + result]) { - result++; - } - return result; - } - } - - static final class UnsafeCommonPrefixer extends CommonPrefixer { - - static { - if (!UNSAFE_UNALIGNED) { - throw new Error(); - } - - // sanity check - this should never fail - if (HBasePlatformDependent.arrayIndexScale(byte[].class) != 1) { - throw new AssertionError(); - } - } - - public UnsafeCommonPrefixer() { - } - - @Override - public int findCommonPrefix(final byte[] left, final int leftOffset, final int leftLength, final byte[] right, - final int rightOffset, final int rightLength) { - final int stride = 8; - final int minLength = Math.min(leftLength, rightLength); - final int strideLimit = minLength & ~(stride - 1); - final long leftOffsetAdj = leftOffset + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - final long rightOffsetAdj = rightOffset + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET; - int result = 0; - int i; - - for (i = 0; i < strideLimit; i += stride) { - final long lw = HBasePlatformDependent.getLong(left, leftOffsetAdj + i); - final long rw = HBasePlatformDependent.getLong(right, rightOffsetAdj + i); - if (lw != rw) { - if (!UnsafeAccess.LITTLE_ENDIAN) { - return result + (Long.numberOfLeadingZeros(lw ^ rw) / Bytes.SIZEOF_LONG); - } else { - return result + (Long.numberOfTrailingZeros(lw ^ rw) / Bytes.SIZEOF_LONG); - } - } else { - result += Bytes.SIZEOF_LONG; - } - } - - // The epilogue to cover the last (minLength % stride) elements. - for (; i < minLength; i++) { - final int il = (left[leftOffset + i]); - final int ir = (right[rightOffset + i]); - if (il != ir) { - return result; - } else { - result++; - } - } - - return result; - } - } - } - - /** - * Lexicographically determine the equality of two arrays. - * - * @param left left operand - * @param right right operand - * @return True if equal - */ - public static boolean equals(final byte[] left, final byte[] right) { - // Could use Arrays.equals? - // noinspection SimplifiableConditionalExpression - if (left == right) { - return true; - } - if (left == null || right == null) { - return false; - } - if (left.length != right.length) { - return false; - } - if (left.length == 0) { - return true; - } - - // Since we're often comparing adjacent sorted data, - // it's usual to have equal arrays except for the very last byte - // so check that first - if (left[left.length - 1] != right[right.length - 1]) { - return false; - } - - return compareTo(left, right) == 0; - } - - /** - * Lexicographically determine the equality of two arrays. - * - * @param left left operand - * @param leftOffset offset into left operand - * @param leftLen length of left operand - * @param right right operand - * @param rightOffset offset into right operand - * @param rightLen length of right operand - * @return True if equal - */ - public static boolean equals(final byte[] left, final int leftOffset, final int leftLen, final byte[] right, - final int rightOffset, final int rightLen) { - // short circuit case - if (left == right && leftOffset == rightOffset && leftLen == rightLen) { - return true; - } - // different lengths fast check - if (leftLen != rightLen) { - return false; - } - if (leftLen == 0) { - return true; - } - - // Since we're often comparing adjacent sorted data, - // it's usual to have equal arrays except for the very last byte - // so check that first - if (left[leftOffset + leftLen - 1] != right[rightOffset + rightLen - 1]) { - return false; - } - - return LexicographicalComparerHolder.BEST_COMPARER.compareTo(left, leftOffset, leftLen, right, - rightOffset, rightLen) == 0; - } - - /** - * Lexicographically determine the equality of two byte[], one as ByteBuffer. - * - * @param a left operand - * @param buf right operand - * @return True if equal - */ - public static boolean equals(final byte[] a, final ByteBuffer buf) { - if (a == null) { - return buf == null; - } - if (buf == null) { - return false; - } - if (a.length != buf.remaining()) { - return false; - } - - // Thou shalt not modify the original byte buffer in what should be read only operations. - final ByteBuffer b = buf.duplicate(); - for (final byte anA : a) { - if (anA != b.get()) { - return false; - } - } - return true; - } - - /** - * Return true if the byte array on the right is a prefix of the byte array on the left. - */ - public static boolean startsWith(final byte[] bytes, final byte[] prefix) { - return bytes != null && prefix != null && bytes.length >= prefix.length - && LexicographicalComparerHolder.BEST_COMPARER.compareTo(bytes, 0, prefix.length, prefix, 0, - prefix.length) == 0; - } - - /** - * Calculate a hash code from a given byte array. - * - * @param b bytes to hash - * @return Runs {@link WritableComparator#hashBytes(byte[], int)} on the passed in array. This - * method is what {@link org.apache.hadoop.io.Text} use calculating hash code. - */ - public static int hashCode(final byte[] b) { - return hashCode(b, b.length); - } - - /** - * Calculate a hash code from a given byte array. - * - * @param b value - * @param length length of the value - * @return Runs {@link WritableComparator#hashBytes(byte[], int)} on the passed in array. This - * method is what {@link org.apache.hadoop.io.Text} use calculating hash code. - */ - public static int hashCode(final byte[] b, final int length) { - return WritableComparator.hashBytes(b, length); - } - - /** - * Calculate a hash code from a given byte array suitable for use as a key in maps. - * - * @param b bytes to hash - * @return A hash of b as an Integer that can be used as key in Maps. - */ - public static Integer mapKey(final byte[] b) { - return hashCode(b); - } - - /** - * Calculate a hash code from a given byte array suitable for use as a key in maps. - * - * @param b bytes to hash - * @param length length to hash - * @return A hash of b as an Integer that can be used as key in Maps. - */ - public static Integer mapKey(final byte[] b, final int length) { - return hashCode(b, length); - } - - /** - * Concatenate byte arrays. - * - * @param a lower half - * @param b upper half - * @return New array that has a in lower half and b in upper half. - */ - public static byte[] add(final byte[] a, final byte[] b) { - return add(a, b, EMPTY_BYTE_ARRAY); - } - - /** - * Concatenate byte arrays. - * - * @param a first third - * @param b second third - * @param c third third - * @return New array made from a, b and c - */ - public static byte[] add(final byte[] a, final byte[] b, final byte[] c) { - final byte[] result = new byte[a.length + b.length + c.length]; - System.arraycopy(a, 0, result, 0, a.length); - System.arraycopy(b, 0, result, a.length, b.length); - System.arraycopy(c, 0, result, a.length + b.length, c.length); - return result; - } - - /** - * Concatenate byte arrays. - * - * @param arrays all the arrays to concatenate together. - * @return New array made from the concatenation of the given arrays. - */ - public static byte[] add(final byte[][] arrays) { - int length = 0; - for (int i = 0; i < arrays.length; i++) { - length += arrays[i].length; - } - final byte[] result = new byte[length]; - int index = 0; - for (int i = 0; i < arrays.length; i++) { - System.arraycopy(arrays[i], 0, result, index, arrays[i].length); - index += arrays[i].length; - } - return result; - } - - /** - * Make a new byte array from a subset of bytes at the head of another. - * - * @param a array - * @param length amount of bytes to grab - * @return First length bytes from a - */ - public static byte[] head(final byte[] a, final int length) { - if (a.length < length) { - return null; - } - final byte[] result = new byte[length]; - System.arraycopy(a, 0, result, 0, length); - return result; - } - - /** - * Make a new byte array from a subset of bytes at the tail of another. - * - * @param a array - * @param length amount of bytes to snarf - * @return Last length bytes from a - */ - public static byte[] tail(final byte[] a, final int length) { - if (a.length < length) { - return null; - } - final byte[] result = new byte[length]; - System.arraycopy(a, a.length - length, result, 0, length); - return result; - } - - /** - * Make a new byte array from a subset of bytes at the head of another, zero padded as desired. - * - * @param a array - * @param length new array size - * @return Value in a plus length prepended 0 bytes - */ - public static byte[] padHead(final byte[] a, final int length) { - final byte[] padding = new byte[length]; - for (int i = 0; i < length; i++) { - padding[i] = 0; - } - return add(padding, a); - } - - /** - * Make a new byte array from a subset of bytes at the tail of another, zero padded as desired. - * - * @param a array - * @param length new array size - * @return Value in a plus length appended 0 bytes - */ - public static byte[] padTail(final byte[] a, final int length) { - final byte[] padding = new byte[length]; - for (int i = 0; i < length; i++) { - padding[i] = 0; - } - return add(a, padding); - } - - /** - * Split passed range. Expensive operation relatively. Uses BigInteger math. Useful splitting - * ranges for MapReduce jobs. - * - * @param a Beginning of range - * @param b End of range - * @param num Number of times to split range. Pass 1 if you want to split the range in two; i.e. - * one split. - * @return Array of dividing values - */ - public static byte[][] split(final byte[] a, final byte[] b, final int num) { - return split(a, b, false, num); - } - - /** - * Split passed range. Expensive operation relatively. Uses BigInteger math. Useful splitting - * ranges for MapReduce jobs. - * - * @param a Beginning of range - * @param b End of range - * @param inclusive Whether the end of range is prefix-inclusive or is considered an exclusive - * boundary. Automatic splits are generally exclusive and manual splits with an - * explicit range utilize an inclusive end of range. - * @param num Number of times to split range. Pass 1 if you want to split the range in two; - * i.e. one split. - * @return Array of dividing values - */ - public static byte[][] split(final byte[] a, final byte[] b, final boolean inclusive, final int num) { - final byte[][] ret = new byte[num + 2][]; - int i = 0; - final Iterable iter = iterateOnSplits(a, b, inclusive, num); - if (iter == null) { - return null; - } - for (final byte[] elem : iter) { - ret[i++] = elem; - } - return ret; - } - - /** - * Iterate over keys within the passed range, splitting at an [a,b) boundary. - */ - public static Iterable iterateOnSplits(final byte[] a, final byte[] b, final int num) { - return iterateOnSplits(a, b, false, num); - } - - /** - * Iterate over keys within the passed range. - */ - public static Iterable iterateOnSplits(final byte[] a, final byte[] b, final boolean inclusive, - final int num) { - final byte[] aPadded; - final byte[] bPadded; - if (a.length < b.length) { - aPadded = padTail(a, b.length - a.length); - bPadded = b; - } else if (b.length < a.length) { - aPadded = a; - bPadded = padTail(b, a.length - b.length); - } else { - aPadded = a; - bPadded = b; - } - if (compareTo(aPadded, bPadded) >= 0) { - throw new IllegalArgumentException("b <= a"); - } - if (num <= 0) { - throw new IllegalArgumentException("num cannot be <= 0"); - } - final byte[] prependHeader = {1, 0}; - final BigInteger startBI = new BigInteger(add(prependHeader, aPadded)); - final BigInteger stopBI = new BigInteger(add(prependHeader, bPadded)); - BigInteger diffBI = stopBI.subtract(startBI); - if (inclusive) { - diffBI = diffBI.add(BigInteger.ONE); - } - final BigInteger splitsBI = BigInteger.valueOf(num + 1); - // when diffBI < splitBI, use an additional byte to increase diffBI - if (diffBI.compareTo(splitsBI) < 0) { - final byte[] aPaddedAdditional = new byte[aPadded.length + 1]; - final byte[] bPaddedAdditional = new byte[bPadded.length + 1]; - for (int i = 0; i < aPadded.length; i++) { - aPaddedAdditional[i] = aPadded[i]; - } - for (int j = 0; j < bPadded.length; j++) { - bPaddedAdditional[j] = bPadded[j]; - } - aPaddedAdditional[aPadded.length] = 0; - bPaddedAdditional[bPadded.length] = 0; - return iterateOnSplits(aPaddedAdditional, bPaddedAdditional, inclusive, num); - } - final BigInteger intervalBI; - try { - intervalBI = diffBI.divide(splitsBI); - } catch (final Exception e) { - LOG.error("Exception caught during division", e); - return null; - } - - final Iterator iterator = new Iterator() { - private int i = -1; - - @Override - public boolean hasNext() { - return i < num + 1; - } - - @Override - public byte[] next() { - i++; - if (i == 0) { - return a; - } - if (i == num + 1) { - return b; - } - - final BigInteger curBI = startBI.add(intervalBI.multiply(BigInteger.valueOf(i))); - byte[] padded = curBI.toByteArray(); - if (padded[1] == 0) { - padded = tail(padded, padded.length - 2); - } else { - padded = tail(padded, padded.length - 1); - } - return padded; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - }; - - return new Iterable() { - @Override - public Iterator iterator() { - return iterator; - } - }; - } - - /** - * Calculate the hash code for a given range of bytes. - * - * @param bytes array to hash - * @param offset offset to start from - * @param length length to hash - */ - public static int hashCode(final byte[] bytes, final int offset, final int length) { - int hash = 1; - for (int i = offset; i < offset + length; i++) { - hash = (31 * hash) + bytes[i]; - } - return hash; - } - - /** - * Create an array of byte[] given an array of String. - * - * @param t operands - * @return Array of byte arrays made from passed array of Text - */ - public static byte[][] toByteArrays(final String[] t) { - final byte[][] result = new byte[t.length][]; - for (int i = 0; i < t.length; i++) { - result[i] = Bytes.toBytes(t[i]); - } - return result; - } - - /** - * Create an array of byte[] given an array of String. - * - * @param t operands - * @return Array of binary byte arrays made from passed array of binary strings - */ - public static byte[][] toBinaryByteArrays(final String[] t) { - final byte[][] result = new byte[t.length][]; - for (int i = 0; i < t.length; i++) { - result[i] = Bytes.toBytesBinary(t[i]); - } - return result; - } - - /** - * Create a byte[][] where first and only entry is column - * - * @param column operand - * @return A byte array of a byte array where first and only entry is column - */ - public static byte[][] toByteArrays(final String column) { - return toByteArrays(toBytes(column)); - } - - /** - * Create a byte[][] where first and only entry is column - * - * @param column operand - * @return A byte array of a byte array where first and only entry is column - */ - public static byte[][] toByteArrays(final byte[] column) { - final byte[][] result = new byte[1][]; - result[0] = column; - return result; - } - - /** - * Binary search for keys in indexes using Bytes.BYTES_RAWCOMPARATOR. - * - * @param arr array of byte arrays to search for - * @param key the key you want to find - * @param offset the offset in the key you want to find - * @param length the length of the key - * @return zero-based index of the key, if the key is present in the array. Otherwise, a value -(i - * + 1) such that the key is between arr[i - 1] and arr[i] non-inclusively, where i is in - * [0, i], if we define arr[-1] = -Inf and arr[N] = Inf for an N-element array. The above - * means that this function can return 2N + 1 different values ranging from -(N + 1) to N - * - 1. - */ - public static int binarySearch(final byte[][] arr, final byte[] key, final int offset, final int length) { - int low = 0; - int high = arr.length - 1; - - while (low <= high) { - final int mid = low + ((high - low) >> 1); - // we have to compare in this order, because the comparator order - // has special logic when the 'left side' is a special key. - final int cmp = - Bytes.BYTES_RAWCOMPARATOR.compare(key, offset, length, arr[mid], 0, arr[mid].length); - // key lives above the midpoint - if (cmp > 0) { - low = mid + 1; - } - // key lives below the midpoint - else if (cmp < 0) { - high = mid - 1; - } - // BAM. how often does this really happen? - else { - return mid; - } - } - return -(low + 1); - } - -// /** -// * Binary search for keys in indexes. -// * -// * @param arr array of byte arrays to search for -// * @param key the key you want to find -// * @param comparator a comparator to compare. -// * @return zero-based index of the key, if the key is present in the array. Otherwise, a value -(i -// * + 1) such that the key is between arr[i - 1] and arr[i] non-inclusively, where i is in -// * [0, i], if we define arr[-1] = -Inf and arr[N] = Inf for an N-element array. The above -// * means that this function can return 2N + 1 different values ranging from -(N + 1) to N -// * - 1. -// * @return the index of the block -// */ -// public static int binarySearch(Cell[] arr, Cell key, CellComparator comparator) { -// int low = 0; -// int high = arr.length - 1; -// while (low <= high) { -// int mid = low + ((high - low) >> 1); -// // we have to compare in this order, because the comparator order -// // has special logic when the 'left side' is a special key. -// int cmp = comparator.compare(key, arr[mid]); -// // key lives above the midpoint -// if (cmp > 0) { -// low = mid + 1; -// } -// // key lives below the midpoint -// else if (cmp < 0) { -// high = mid - 1; -// } -// // BAM. how often does this really happen? -// else { -// return mid; -// } -// } -// return -(low + 1); -// } - - /** - * Bytewise binary increment/deincrement of long contained in byte array on given amount. - * - * @param value - array of bytes containing long (length <= SIZEOF_LONG) - * @param amount value will be incremented on (deincremented if negative) - * @return array of bytes containing incremented long (length == SIZEOF_LONG) - */ - public static byte[] incrementBytes(final byte[] value, final long amount) { - byte[] val = value; - if (val.length < SIZEOF_LONG) { - // Hopefully this doesn't happen too often. - final byte[] newvalue; - if (val[0] < 0) { - newvalue = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1}; - } else { - newvalue = new byte[SIZEOF_LONG]; - } - System.arraycopy(val, 0, newvalue, newvalue.length - val.length, val.length); - val = newvalue; - } else if (val.length > SIZEOF_LONG) { - throw new IllegalArgumentException("Increment Bytes - value too big: " + val.length); - } - if (amount == 0) { - return val; - } - if (val[0] < 0) { - return binaryIncrementNeg(val, amount); - } - return binaryIncrementPos(val, amount); - } - - /* increment/deincrement for positive value */ - private static byte[] binaryIncrementPos(final byte[] value, final long amount) { - long amo = amount; - int sign = 1; - if (amount < 0) { - amo = -amount; - sign = -1; - } - for (int i = 0; i < value.length; i++) { - final int cur = ((int) amo % 256) * sign; - amo = (amo >> 8); - final int val = value[value.length - i - 1] & 0x0ff; - int total = val + cur; - if (total > 255) { - amo += sign; - total %= 256; - } else if (total < 0) { - amo -= sign; - } - value[value.length - i - 1] = (byte) total; - if (amo == 0) { - return value; - } - } - return value; - } - - /* increment/deincrement for negative value */ - private static byte[] binaryIncrementNeg(final byte[] value, final long amount) { - long amo = amount; - int sign = 1; - if (amount < 0) { - amo = -amount; - sign = -1; - } - for (int i = 0; i < value.length; i++) { - final int cur = ((int) amo % 256) * sign; - amo = (amo >> 8); - final int val = (~value[value.length - i - 1] & 0x0ff) + 1; - int total = cur - val; - if (total >= 0) { - amo += sign; - } else if (total < -256) { - amo -= sign; - total %= 256; - } - value[value.length - i - 1] = (byte) total; - if (amo == 0) { - return value; - } - } - return value; - } - - /** - * Writes a string as a fixed-size field, padded with zeros. - */ - public static void writeStringFixedSize(final DataOutput out, final String s, final int size) - throws IOException { - final byte[] b = toBytes(s); - if (b.length > size) { - throw new IOException("Trying to write " + b.length + " bytes (" + toStringBinary(b) - + ") into a field of length " + size); - } - - out.writeBytes(s); - for (int i = 0; i < size - s.length(); ++i) { - out.writeByte(0); - } - } - - /** - * Reads a fixed-size field and interprets it as a string padded with zeros. - */ - public static String readStringFixedSize(final DataInput in, final int size) throws IOException { - final byte[] b = new byte[size]; - in.readFully(b); - int n = b.length; - while (n > 0 && b[n - 1] == 0) { - --n; - } - - return toString(b, 0, n); - } - - /** - * Copy the byte array given in parameter and return an instance of a new byte array with the same - * length and the same content. - * - * @param bytes the byte array to duplicate - * @return a copy of the given byte array - */ - public static byte[] copy(final byte[] bytes) { - if (bytes == null) { - return null; - } - final byte[] result = new byte[bytes.length]; - System.arraycopy(bytes, 0, result, 0, bytes.length); - return result; - } - - /** - * Copy the byte array given in parameter and return an instance of a new byte array with the same - * length and the same content. - * - * @param bytes the byte array to copy from - * @return a copy of the given designated byte array - */ - public static byte[] copy(final byte[] bytes, final int offset, final int length) { - if (bytes == null) { - return null; - } - final byte[] result = new byte[length]; - System.arraycopy(bytes, offset, result, 0, length); - return result; - } - - /** - * Search sorted array "a" for byte "key". I can't remember if I wrote this or copied it from - * somewhere. (mcorgan) - * - * @param a Array to search. Entries must be sorted and unique. - * @param fromIndex First index inclusive of "a" to include in the search. - * @param toIndex Last index exclusive of "a" to include in the search. - * @param key The byte to search for. - * @return The index of key if found. If not found, return -(index + 1), where negative indicates - * "not found" and the "index + 1" handles the "-0" case. - */ - public static int unsignedBinarySearch(final byte[] a, final int fromIndex, final int toIndex, final byte key) { - final int unsignedKey = key & 0xff; - int low = fromIndex; - int high = toIndex - 1; - - while (low <= high) { - final int mid = low + ((high - low) >> 1); - final int midVal = a[mid] & 0xff; - - if (midVal < unsignedKey) { - low = mid + 1; - } else if (midVal > unsignedKey) { - high = mid - 1; - } else { - return mid; // key found - } - } - return -(low + 1); // key not found. - } - - /** - * Treat the byte[] as an unsigned series of bytes, most significant bits first. Start by adding 1 - * to the rightmost bit/byte and carry over all overflows to the more significant bits/bytes. - * - * @param input The byte[] to increment. - * @return The incremented copy of "in". May be same length or 1 byte longer. - */ - public static byte[] unsignedCopyAndIncrement(final byte[] input) { - final byte[] copy = copy(input); - if (copy == null) { - throw new IllegalArgumentException("cannot increment null array"); - } - for (int i = copy.length - 1; i >= 0; --i) { - if (copy[i] == -1) {// -1 is all 1-bits, which is the unsigned maximum - copy[i] = 0; - } else { - ++copy[i]; - return copy; - } - } - // we maxed out the array - final byte[] out = new byte[copy.length + 1]; - out[0] = 1; - System.arraycopy(copy, 0, out, 1, copy.length); - return out; - } - - public static boolean equals(final List a, final List b) { - if (a == null) { - if (b == null) { - return true; - } - return false; - } - if (b == null) { - return false; - } - if (a.size() != b.size()) { - return false; - } - for (int i = 0; i < a.size(); ++i) { - if (!Bytes.equals(a.get(i), b.get(i))) { - return false; - } - } - return true; - } - -// public static boolean isSorted(Collection arrays) { -// if (!CollectionUtils.isEmpty(arrays)) { -// byte[] previous = new byte[0]; -// for (byte[] array : arrays) { -// if (Bytes.compareTo(previous, array) > 0) { -// return false; -// } -// previous = array; -// } -// } -// return true; -// } - - public static List getUtf8ByteArrays(final List strings) { - if (strings.isEmpty()) { - return Collections.emptyList(); - } - final List byteArrays = new ArrayList<>(strings.size()); - strings.forEach(s -> byteArrays.add(Bytes.toBytes(s))); - return byteArrays; - } - - /** - * Returns the index of the first appearance of the value {@code target} in {@code array}. - * - * @param array an array of {@code byte} values, possibly empty - * @param target a primitive {@code byte} value - * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no - * such index exists. - */ - public static int indexOf(final byte[] array, final byte target) { - for (int i = 0; i < array.length; i++) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * 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. - *

- * 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 array the array to search for the sequence {@code target} - * @param target the array to search for as a sub-sequence of {@code array} - */ - public static int indexOf(final byte[] array, final byte[] target) { - Preconditions.checkNotNull(array, "array"); - Preconditions.checkNotNull(target, "target"); - if (target.length == 0) { - return 0; - } - - outer: - for (int i = 0; i < array.length - target.length + 1; i++) { - for (int j = 0; j < target.length; j++) { - if (array[i + j] != target[j]) { - continue outer; - } - } - return i; - } - return -1; - } - - /** - * Return true if target is present as an element anywhere in the given array. - * - * @param array an array of {@code byte} values, possibly empty - * @param target a primitive {@code byte} value - * @return {@code true} if {@code target} is present as an element anywhere in {@code array}. - */ - public static boolean contains(final byte[] array, final byte target) { - return indexOf(array, target) > -1; - } - - /** - * Return true if target is present as an element anywhere in the given array. - * - * @param array an array of {@code byte} values, possibly empty - * @param target an array of {@code byte} - * @return {@code true} if {@code target} is present anywhere in {@code array} - */ - public static boolean contains(final byte[] array, final byte[] target) { - return indexOf(array, target) > -1; - } - - /** - * Fill given array with zeros. - * - * @param b array which needs to be filled with zeros - */ - public static void zero(final byte[] b) { - zero(b, 0, b.length); - } - - /** - * Fill given array with zeros at the specified position. - */ - public static void zero(final byte[] b, final int offset, final int length) { - Preconditions.checkPositionIndex(offset, b.length, "offset"); - Preconditions.checkArgument(length > 0, "length must be greater than 0"); - Preconditions.checkPositionIndex(offset + length, b.length, "offset + length"); - Arrays.fill(b, offset, offset + length, (byte) 0); - } - - // Pseudorandom random number generator, do not use SecureRandom here - private static final Random RNG = new Random(); - - /** - * Fill given array with random bytes. - * - * @param b array which needs to be filled with random bytes - *

- * If you want random bytes generated by a strong source of randomness use - * {@link Bytes#secureRandom(byte[])}. - * @param b array which needs to be filled with random bytes - */ - public static void random(final byte[] b) { - RNG.nextBytes(b); - } - - /** - * Fill given array with random bytes at the specified position. - *

- * If you want random bytes generated by a strong source of randomness use - * {@link Bytes#secureRandom(byte[], int, int)}. - * - * @param b array which needs to be filled with random bytes - * @param offset staring offset in array - * @param length number of bytes to fill - */ - public static void random(final byte[] b, final int offset, final int length) { - Preconditions.checkPositionIndex(offset, b.length, "offset"); - Preconditions.checkArgument(length > 0, "length must be greater than 0"); - Preconditions.checkPositionIndex(offset + length, b.length, "offset + length"); - final byte[] buf = new byte[length]; - RNG.nextBytes(buf); - System.arraycopy(buf, 0, b, offset, length); - } - - // Bytes.secureRandom may be used to create key material. - private static final SecureRandom SECURE_RNG = new SecureRandom(); - - /** - * Fill given array with random bytes using a strong random number generator. - * - * @param b array which needs to be filled with random bytes - */ - public static void secureRandom(final byte[] b) { - SECURE_RNG.nextBytes(b); - } - - /** - * Fill given array with random bytes at the specified position using a strong random number - * generator. - * - * @param b array which needs to be filled with random bytes - * @param offset staring offset in array - * @param length number of bytes to fill - */ - public static void secureRandom(final byte[] b, final int offset, final int length) { - Preconditions.checkPositionIndex(offset, b.length, "offset"); - Preconditions.checkArgument(length > 0, "length must be greater than 0"); - Preconditions.checkPositionIndex(offset + length, b.length, "offset + length"); - final byte[] buf = new byte[length]; - SECURE_RNG.nextBytes(buf); - System.arraycopy(buf, 0, b, offset, length); - } - - /** - * Create a max byte array with the specified max byte count - * - * @param maxByteCount the length of returned byte array - * @return the created max byte array - */ - public static byte[] createMaxByteArray(final int maxByteCount) { - final byte[] maxByteArray = new byte[maxByteCount]; - for (int i = 0; i < maxByteArray.length; i++) { - maxByteArray[i] = (byte) 0xff; - } - return maxByteArray; - } - - /** - * Create a byte array which is multiple given bytes - * - * @return byte array - */ - public static byte[] multiple(final byte[] srcBytes, final int multiNum) { - if (multiNum <= 0) { - return new byte[0]; - } - final byte[] result = new byte[srcBytes.length * multiNum]; - for (int i = 0; i < multiNum; i++) { - System.arraycopy(srcBytes, 0, result, i * srcBytes.length, srcBytes.length); - } - return result; - } - - private static final char[] HEX_CHARS = - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - /** - * Convert a byte range into a hex string - */ - public static String toHex(final byte[] b, final int offset, final int length) { - Preconditions.checkArgument(length <= Integer.MAX_VALUE / 2); - final int numChars = length * 2; - final char[] ch = new char[numChars]; - for (int i = 0; i < numChars; i += 2) { - final byte d = b[offset + i / 2]; - ch[i] = HEX_CHARS[(d >> 4) & 0x0F]; - ch[i + 1] = HEX_CHARS[d & 0x0F]; - } - return new String(ch); - } - - /** - * Convert a byte array into a hex string - */ - public static String toHex(final byte[] b) { - return toHex(b, 0, b.length); - } - - private static int hexCharToNibble(final char ch) { - if (ch <= '9' && ch >= '0') { - return ch - '0'; - } else if (ch >= 'a' && ch <= 'f') { - return ch - 'a' + 10; - } else if (ch >= 'A' && ch <= 'F') { - return ch - 'A' + 10; - } - throw new IllegalArgumentException("Invalid hex char: " + ch); - } - - private static byte hexCharsToByte(final char c1, final char c2) { - return (byte) ((hexCharToNibble(c1) << 4) | hexCharToNibble(c2)); - } - - /** - * Create a byte array from a string of hash digits. The length of the string must be a multiple - * of 2 - */ - public static byte[] fromHex(final String hex) { - Preconditions.checkArgument(hex.length() % 2 == 0, "length must be a multiple of 2"); - final int len = hex.length(); - final byte[] b = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - b[i / 2] = hexCharsToByte(hex.charAt(i), hex.charAt(i + 1)); - } - return b; - } - - /** - * Find index of passed delimiter. - * - * @return Index of delimiter having started from start of b moving rightward. - */ - public static int searchDelimiterIndex(final byte[] b, final int offset, final int length, - final int delimiter) { - if (b == null) { - throw new IllegalArgumentException("Passed buffer is null"); - } - int result = -1; - for (int i = offset; i < length + offset; i++) { - if (b[i] == delimiter) { - result = i; - break; - } - } - return result; - } - - /** - * Find index of passed delimiter walking from end of buffer backwards. - * - * @return Index of delimiter - */ - public static int searchDelimiterIndexInReverse(final byte[] b, final int offset, - final int length, final int delimiter) { - if (b == null) { - throw new IllegalArgumentException("Passed buffer is null"); - } - int result = -1; - for (int i = (offset + length) - 1; i >= offset; i--) { - if (b[i] == delimiter) { - result = i; - break; - } - } - return result; - } - - public static int findCommonPrefix(final byte[] left, final byte[] right, final int leftLength, final int rightLength, - final int leftOffset, final int rightOffset) { - return CommonPrefixerHolder.BEST_COMMON_PREFIXER.findCommonPrefix(left, leftOffset, leftLength, - right, rightOffset, rightLength); - } -} diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBasePlatformDependent.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBasePlatformDependent.java deleted file mode 100644 index dbbaa9ed73d..00000000000 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBasePlatformDependent.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copy of https://github.com/apache/hbase-thirdparty/blob/master/hbase-unsafe/src/main/java/org/apache/hadoop/hbase/unsafe/HBasePlatformDependent.java - * to avoid having to pull in all of hbase to use the util methods. - * - * 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 - * - * 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. - */ - -package stroom.bytebuffer.hbase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.function.BiConsumer; - -/** - * Delegate all methods of {@link HBaseUnsafeInternal} and {@link HBaseSignalInternal} so we will - * not touch the actual {@code sun.misc.Unsafe} and {@code sun.misc.Signal} classes until we - * actually call the methods. - */ -public final class HBasePlatformDependent { - - private static final String CLASS_NAME = "sun.misc.Unsafe"; - private static final Logger LOG = LoggerFactory.getLogger(HBasePlatformDependent.class); - private static final boolean AVAIL; - private static final boolean UNALIGNED; - - static { - AVAIL = AccessController.doPrivileged(new PrivilegedAction() { - - @Override - public Boolean run() { - return checkAvailable(); - } - }); - UNALIGNED = checkUnaligned(); - } - - private static boolean checkAvailable() { - try { - final Class clazz = Class.forName(CLASS_NAME); - final Field f = clazz.getDeclaredField("theUnsafe"); - f.setAccessible(true); - final Object theUnsafe = f.get(null); - if (theUnsafe == null) { - LOG.warn("Could not get static instance from sun.misc.Unsafe"); - return false; - } - // Check for availability of all methods used by UnsafeAccess - Method m; - try { - m = clazz.getDeclaredMethod("arrayBaseOffset", Class.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing arrayBaseOffset(Class)"); - return false; - } - m = clazz.getDeclaredMethod("copyMemory", Object.class, long.class, Object.class, - long.class, long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing copyMemory(Object,long,Object,long,long)"); - return false; - } - m = clazz.getDeclaredMethod("getByte", Object.class, long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing getByte(Object,long)"); - return false; - } - m = clazz.getDeclaredMethod("getShort", long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing getShort(long)"); - return false; - } - m = clazz.getDeclaredMethod("getShort", Object.class, long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing getShort(Object,long)"); - return false; - } - m = clazz.getDeclaredMethod("getInt", long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing getInt(long)"); - return false; - } - m = clazz.getDeclaredMethod("getInt", Object.class, long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing getInt(Object,long)"); - return false; - } - m = clazz.getDeclaredMethod("getLong", long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing getLong(long)"); - return false; - } - m = clazz.getDeclaredMethod("getLong", Object.class, long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing getLong(Object,long)"); - return false; - } - m = clazz.getDeclaredMethod("putByte", long.class, byte.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putByte(long,byte)"); - return false; - } - m = clazz.getDeclaredMethod("putByte", Object.class, long.class, byte.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putByte(Object,long,byte)"); - return false; - } - m = clazz.getDeclaredMethod("putShort", long.class, short.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putShort(long,short)"); - return false; - } - m = clazz.getDeclaredMethod("putShort", Object.class, long.class, short.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putShort(Object,long,short)"); - return false; - } - m = clazz.getDeclaredMethod("putInt", long.class, int.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putInt(long,int)"); - return false; - } - m = clazz.getDeclaredMethod("putInt", Object.class, long.class, int.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putInt(Object,long,int)"); - return false; - } - m = clazz.getDeclaredMethod("putLong", long.class, long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putLong(long,long)"); - return false; - } - m = clazz.getDeclaredMethod("putLong", Object.class, long.class, long.class); - if (m == null) { - LOG.warn("sun.misc.Unsafe is missing putLong(Object,long,long)"); - return false; - } - // theUnsafe is accessible and all methods are available - return true; - } catch (final Throwable e) { - LOG.warn("sun.misc.Unsafe is missing one or more required methods", e); - } - } catch (final Throwable e) { - LOG.warn("sun.misc.Unsafe is not available/accessible", e); - } - return false; - } - - private static boolean checkUnaligned() { - // When Unsafe itself is not available/accessible consider unaligned as false. - if (!AVAIL) { - return false; - } - final String arch = System.getProperty("os.arch"); - if ("ppc64".equals(arch) || "ppc64le".equals(arch) || "aarch64".equals(arch)) { - // java.nio.Bits.unaligned() wrongly returns false on ppc (JDK-8165231), - return true; - } - try { - // Using java.nio.Bits#unaligned() to check for unaligned-access capability - final Class clazz = Class.forName("java.nio.Bits"); - final Method m = clazz.getDeclaredMethod("unaligned"); - m.setAccessible(true); - return (Boolean) m.invoke(null); - } catch (final Exception e) { - LOG.warn("java.nio.Bits#unaligned() check failed." - + "Unsafe based read/write of primitive types won't be used", e); - } - return false; - } - - /** - * @return true when running JVM is having sun's Unsafe package available in it and it is - * accessible. - */ - public static boolean isUnsafeAvailable() { - return AVAIL; - } - - /** - * @return true when running JVM is having sun's Unsafe package available in it and underlying - * system having unaligned-access capability. - */ - public static boolean unaligned() { - return UNALIGNED; - } - - private HBasePlatformDependent() { - // private constructor to avoid instantiation - } - - public static int getInt(final Object o, final long offset) { - return HBaseUnsafeInternal.getInt(o, offset); - } - - public static void putInt(final Object o, final long offset, final int x) { - HBaseUnsafeInternal.putInt(o, offset, x); - } - - public static Object getObject(final Object o, final long offset) { - return HBaseUnsafeInternal.getObject(o, offset); - } - - public static void putObject(final Object o, final long offset, final Object x) { - HBaseUnsafeInternal.putObject(o, offset, x); - } - - public static boolean getBoolean(final Object o, final long offset) { - return HBaseUnsafeInternal.getBoolean(o, offset); - } - - public static void putBoolean(final Object o, final long offset, final boolean x) { - HBaseUnsafeInternal.putBoolean(o, offset, x); - } - - public static byte getByte(final Object o, final long offset) { - return HBaseUnsafeInternal.getByte(o, offset); - } - - public static void putByte(final Object o, final long offset, final byte x) { - HBaseUnsafeInternal.putByte(o, offset, x); - } - - public static short getShort(final Object o, final long offset) { - return HBaseUnsafeInternal.getShort(o, offset); - } - - public static void putShort(final Object o, final long offset, final short x) { - HBaseUnsafeInternal.putShort(o, offset, x); - } - - public static char getChar(final Object o, final long offset) { - return HBaseUnsafeInternal.getChar(o, offset); - } - - public static void putChar(final Object o, final long offset, final char x) { - HBaseUnsafeInternal.putChar(o, offset, x); - } - - public static long getLong(final Object o, final long offset) { - return HBaseUnsafeInternal.getLong(o, offset); - } - - public static void putLong(final Object o, final long offset, final long x) { - HBaseUnsafeInternal.putLong(o, offset, x); - } - - public static float getFloat(final Object o, final long offset) { - return HBaseUnsafeInternal.getFloat(o, offset); - } - - public static void putFloat(final Object o, final long offset, final float x) { - HBaseUnsafeInternal.putFloat(o, offset, x); - } - - public static double getDouble(final Object o, final long offset) { - return HBaseUnsafeInternal.getDouble(o, offset); - } - - public static void putDouble(final Object o, final long offset, final double x) { - HBaseUnsafeInternal.putDouble(o, offset, x); - } - - public static byte getByte(final long address) { - return HBaseUnsafeInternal.getByte(address); - } - - public static void putByte(final long address, final byte x) { - HBaseUnsafeInternal.putByte(address, x); - } - - public static short getShort(final long address) { - return HBaseUnsafeInternal.getShort(address); - } - - public static void putShort(final long address, final short x) { - HBaseUnsafeInternal.putShort(address, x); - } - - public static char getChar(final long address) { - return HBaseUnsafeInternal.getChar(address); - } - - public static void putChar(final long address, final char x) { - HBaseUnsafeInternal.putChar(address, x); - } - - public static int getInt(final long address) { - return HBaseUnsafeInternal.getInt(address); - } - - public static void putInt(final long address, final int x) { - HBaseUnsafeInternal.putInt(address, x); - } - - public static long getLong(final long address) { - return HBaseUnsafeInternal.getLong(address); - } - - public static void putLong(final long address, final long x) { - HBaseUnsafeInternal.putLong(address, x); - } - - public static float getFloat(final long address) { - return HBaseUnsafeInternal.getFloat(address); - } - - public static void putFloat(final long address, final float x) { - HBaseUnsafeInternal.putFloat(address, x); - } - - public static double getDouble(final long address) { - return HBaseUnsafeInternal.getDouble(address); - } - - public static void putDouble(final long address, final double x) { - HBaseUnsafeInternal.putDouble(address, x); - } - - public static long getAddress(final long address) { - return HBaseUnsafeInternal.getAddress(address); - } - - public static void putAddress(final long address, final long x) { - HBaseUnsafeInternal.putAddress(address, x); - } - - public static long allocateMemory(final long bytes) { - return HBaseUnsafeInternal.allocateMemory(bytes); - } - - public static long reallocateMemory(final long address, final long bytes) { - return HBaseUnsafeInternal.reallocateMemory(address, bytes); - } - - public static void setMemory(final Object o, final long offset, final long bytes, final byte value) { - HBaseUnsafeInternal.setMemory(o, offset, bytes, value); - } - - public static void setMemory(final long address, final long bytes, final byte value) { - HBaseUnsafeInternal.setMemory(address, bytes, value); - } - - public static void copyMemory(final Object srcBase, final long srcOffset, final Object destBase, final long destOffset, - final long bytes) { - HBaseUnsafeInternal.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes); - } - - public static void copyMemory(final long srcAddress, final long destAddress, final long bytes) { - HBaseUnsafeInternal.copyMemory(srcAddress, destAddress, bytes); - } - - public static void freeMemory(final long address) { - HBaseUnsafeInternal.freeMemory(address); - } - - public static long staticFieldOffset(final Field f) { - return HBaseUnsafeInternal.staticFieldOffset(f); - } - - public static long objectFieldOffset(final Field f) { - return HBaseUnsafeInternal.objectFieldOffset(f); - } - - public static Object staticFieldBase(final Field f) { - return HBaseUnsafeInternal.staticFieldBase(f); - } - - public static boolean shouldBeInitialized(final Class c) { - return HBaseUnsafeInternal.shouldBeInitialized(c); - } - - public static void ensureClassInitialized(final Class c) { - HBaseUnsafeInternal.ensureClassInitialized(c); - } - - public static int arrayBaseOffset(final Class arrayClass) { - return HBaseUnsafeInternal.arrayBaseOffset(arrayClass); - } - - public static int arrayIndexScale(final Class arrayClass) { - return HBaseUnsafeInternal.arrayIndexScale(arrayClass); - } - - public static int addressSize() { - return HBaseUnsafeInternal.addressSize(); - } - - public static int pageSize() { - return HBaseUnsafeInternal.pageSize(); - } - -// public static Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, -// ProtectionDomain protectionDomain) { -// return HBaseUnsafeInternal.defineClass(name, b, off, len, loader, protectionDomain); -// } - -// public static Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches) { -// return HBaseUnsafeInternal.defineAnonymousClass(hostClass, data, cpPatches); -// } - - public static Object allocateInstance(final Class cls) throws InstantiationException { - return HBaseUnsafeInternal.allocateInstance(cls); - } - - public static void throwException(final Throwable ee) { - HBaseUnsafeInternal.throwException(ee); - } - - public static boolean compareAndSwapObject(final Object o, final long offset, final Object expected, final Object x) { - return HBaseUnsafeInternal.compareAndSwapObject(o, offset, expected, x); - } - - public static boolean compareAndSwapInt(final Object o, final long offset, final int expected, final int x) { - return HBaseUnsafeInternal.compareAndSwapInt(o, offset, expected, x); - } - - public static boolean compareAndSwapLong(final Object o, final long offset, final long expected, final long x) { - return HBaseUnsafeInternal.compareAndSwapLong(o, offset, expected, x); - } - - public static Object getObjectVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getObjectVolatile(o, offset); - } - - public static void putObjectVolatile(final Object o, final long offset, final Object x) { - HBaseUnsafeInternal.putObjectVolatile(o, offset, x); - } - - public static int getIntVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getIntVolatile(o, offset); - } - - public static void putIntVolatile(final Object o, final long offset, final int x) { - HBaseUnsafeInternal.putIntVolatile(o, offset, x); - } - - public static boolean getBooleanVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getBooleanVolatile(o, offset); - } - - public static void putBooleanVolatile(final Object o, final long offset, final boolean x) { - HBaseUnsafeInternal.putBooleanVolatile(o, offset, x); - } - - public static byte getByteVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getByteVolatile(o, offset); - } - - public static void putByteVolatile(final Object o, final long offset, final byte x) { - HBaseUnsafeInternal.putByteVolatile(o, offset, x); - } - - public static short getShortVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getShortVolatile(o, offset); - } - - public static void putShortVolatile(final Object o, final long offset, final short x) { - HBaseUnsafeInternal.putShortVolatile(o, offset, x); - } - - public static char getCharVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getCharVolatile(o, offset); - } - - public static void putCharVolatile(final Object o, final long offset, final char x) { - HBaseUnsafeInternal.putCharVolatile(o, offset, x); - } - - public static long getLongVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getLongVolatile(o, offset); - } - - public static void putLongVolatile(final Object o, final long offset, final long x) { - HBaseUnsafeInternal.putLongVolatile(o, offset, x); - } - - public static float getFloatVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getFloatVolatile(o, offset); - } - - public static void putFloatVolatile(final Object o, final long offset, final float x) { - HBaseUnsafeInternal.putFloatVolatile(o, offset, x); - } - - public static double getDoubleVolatile(final Object o, final long offset) { - return HBaseUnsafeInternal.getDoubleVolatile(o, offset); - } - - public static void putDoubleVolatile(final Object o, final long offset, final double x) { - HBaseUnsafeInternal.putDoubleVolatile(o, offset, x); - } - - public static void putOrderedObject(final Object o, final long offset, final Object x) { - HBaseUnsafeInternal.putOrderedObject(o, offset, x); - } - - public static void putOrderedInt(final Object o, final long offset, final int x) { - HBaseUnsafeInternal.putOrderedInt(o, offset, x); - } - - public static void putOrderedLong(final Object o, final long offset, final long x) { - HBaseUnsafeInternal.putOrderedLong(o, offset, x); - } - - public static void unpark(final Object thread) { - HBaseUnsafeInternal.unpark(thread); - } - - public static void park(final boolean isAbsolute, final long time) { - HBaseUnsafeInternal.park(isAbsolute, time); - } - - public static int getLoadAverage(final double[] loadavg, final int nelems) { - return HBaseUnsafeInternal.getLoadAverage(loadavg, nelems); - } - - public static int getAndAddInt(final Object o, final long offset, final int delta) { - return HBaseUnsafeInternal.getAndAddInt(o, offset, delta); - } - - public static long getAndAddLong(final Object o, final long offset, final long delta) { - return HBaseUnsafeInternal.getAndAddLong(o, offset, delta); - } - - public static int getAndSetInt(final Object o, final long offset, final int newValue) { - return HBaseUnsafeInternal.getAndSetInt(o, offset, newValue); - } - - public static long getAndSetLong(final Object o, final long offset, final long newValue) { - return HBaseUnsafeInternal.getAndSetLong(o, offset, newValue); - } - - public static Object getAndSetObject(final Object o, final long offset, final Object newValue) { - return HBaseUnsafeInternal.getAndSetObject(o, offset, newValue); - } - - public static void loadFence() { - HBaseUnsafeInternal.loadFence(); - } - - public static void storeFence() { - HBaseUnsafeInternal.storeFence(); - } - - public static void fullFence() { - HBaseUnsafeInternal.fullFence(); - } - - /** - * Delegate {@code sun.misc.Signal}. - * - * @param signal the name of the signal, such as 'HUP'. - * @param handler the handler of the signal, the first parameter is the number of the signal, - * while the second one is the name of the sinal. - */ - public static void handle(final String signal, final BiConsumer handler) { - HBaseSignalInternal.handle(signal, handler); - } -} diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseSignalInternal.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseSignalInternal.java deleted file mode 100644 index 7bb87dd9332..00000000000 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseSignalInternal.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copy of https://github.com/apache/hbase-thirdparty/blob/master/hbase-unsafe/src/main/java/org/apache/hadoop/hbase/unsafe/HBaseSignalInternal.java - * to avoid having to pull in all of hbase to use the util methods. - * - * 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 - * - * 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. - */ - -package stroom.bytebuffer.hbase; - -import sun.misc.Signal; - -import java.util.function.BiConsumer; - -/** - * Delegation of {@code sun.misc.Signal}. - */ -@SuppressWarnings("restriction") -public final class HBaseSignalInternal { - - private HBaseSignalInternal() { - } - - public static void handle(final String signal, final BiConsumer handler) { - Signal.handle(new Signal(signal), s -> handler.accept(s.getNumber(), s.getName())); - } -} diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseUnsafeInternal.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseUnsafeInternal.java deleted file mode 100644 index ca07d4494b3..00000000000 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/HBaseUnsafeInternal.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copy of https://github.com/apache/hbase-thirdparty/blob/master/hbase-unsafe/src/main/java/org/apache/hadoop/hbase/unsafe/HBaseUnsafeInternal.java - * to avoid having to pull in all of hbase to use the util methods. - * - * 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 - * - * 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. - */ - -package stroom.bytebuffer.hbase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sun.misc.Unsafe; - -import java.lang.reflect.Field; -import java.security.AccessController; -import java.security.PrivilegedAction; - -/** - * Delegate all the method in sun.misc.Unsafe. - */ -@SuppressWarnings("restriction") -final class HBaseUnsafeInternal { - - private static final Logger LOG = LoggerFactory.getLogger(HBaseUnsafeInternal.class); - - private static final Unsafe UNSAFE; - - static { - UNSAFE = (Unsafe) AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Object run() { - try { - final Field f = Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return f.get(null); - } catch (final Throwable e) { - LOG.warn("sun.misc.Unsafe is not accessible", e); - } - return null; - } - }); - } - - private HBaseUnsafeInternal() { - } - - public static int getInt(final Object o, final long offset) { - return UNSAFE.getInt(o, offset); - } - - public static void putInt(final Object o, final long offset, final int x) { - UNSAFE.putInt(o, offset, x); - } - - public static Object getObject(final Object o, final long offset) { - return UNSAFE.getObject(o, offset); - } - - public static void putObject(final Object o, final long offset, final Object x) { - UNSAFE.putObject(o, offset, x); - } - - public static boolean getBoolean(final Object o, final long offset) { - return UNSAFE.getBoolean(o, offset); - } - - public static void putBoolean(final Object o, final long offset, final boolean x) { - UNSAFE.putBoolean(o, offset, x); - } - - public static byte getByte(final Object o, final long offset) { - return UNSAFE.getByte(o, offset); - } - - public static void putByte(final Object o, final long offset, final byte x) { - UNSAFE.putByte(o, offset, x); - } - - public static short getShort(final Object o, final long offset) { - return UNSAFE.getShort(o, offset); - } - - public static void putShort(final Object o, final long offset, final short x) { - UNSAFE.putShort(o, offset, x); - } - - public static char getChar(final Object o, final long offset) { - return UNSAFE.getChar(o, offset); - } - - public static void putChar(final Object o, final long offset, final char x) { - UNSAFE.putChar(o, offset, x); - } - - public static long getLong(final Object o, final long offset) { - return UNSAFE.getLong(o, offset); - } - - public static void putLong(final Object o, final long offset, final long x) { - UNSAFE.putLong(o, offset, x); - } - - public static float getFloat(final Object o, final long offset) { - return UNSAFE.getFloat(o, offset); - } - - public static void putFloat(final Object o, final long offset, final float x) { - UNSAFE.putFloat(o, offset, x); - } - - public static double getDouble(final Object o, final long offset) { - return UNSAFE.getDouble(o, offset); - } - - public static void putDouble(final Object o, final long offset, final double x) { - UNSAFE.putDouble(o, offset, x); - } - - public static byte getByte(final long address) { - return UNSAFE.getByte(address); - } - - public static void putByte(final long address, final byte x) { - UNSAFE.putByte(address, x); - } - - public static short getShort(final long address) { - return UNSAFE.getShort(address); - } - - public static void putShort(final long address, final short x) { - UNSAFE.putShort(address, x); - } - - public static char getChar(final long address) { - return UNSAFE.getChar(address); - } - - public static void putChar(final long address, final char x) { - UNSAFE.putChar(address, x); - } - - public static int getInt(final long address) { - return UNSAFE.getInt(address); - } - - public static void putInt(final long address, final int x) { - UNSAFE.putInt(address, x); - } - - public static long getLong(final long address) { - return UNSAFE.getLong(address); - } - - public static void putLong(final long address, final long x) { - UNSAFE.putLong(address, x); - } - - public static float getFloat(final long address) { - return UNSAFE.getFloat(address); - } - - public static void putFloat(final long address, final float x) { - UNSAFE.putFloat(address, x); - } - - public static double getDouble(final long address) { - return UNSAFE.getDouble(address); - } - - public static void putDouble(final long address, final double x) { - UNSAFE.putDouble(address, x); - } - - public static long getAddress(final long address) { - return UNSAFE.getAddress(address); - } - - public static void putAddress(final long address, final long x) { - UNSAFE.putAddress(address, x); - } - - public static long allocateMemory(final long bytes) { - return UNSAFE.allocateMemory(bytes); - } - - public static long reallocateMemory(final long address, final long bytes) { - return UNSAFE.reallocateMemory(address, bytes); - } - - public static void setMemory(final Object o, final long offset, final long bytes, final byte value) { - UNSAFE.setMemory(o, offset, bytes, value); - } - - public static void setMemory(final long address, final long bytes, final byte value) { - UNSAFE.setMemory(address, bytes, value); - } - - public static void copyMemory(final Object srcBase, final long srcOffset, final Object destBase, final long destOffset, - final long bytes) { - UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes); - } - - public static void copyMemory(final long srcAddress, final long destAddress, final long bytes) { - UNSAFE.copyMemory(srcAddress, destAddress, bytes); - } - - public static void freeMemory(final long address) { - UNSAFE.freeMemory(address); - } - - public static long staticFieldOffset(final Field f) { - return UNSAFE.staticFieldOffset(f); - } - - public static long objectFieldOffset(final Field f) { - return UNSAFE.objectFieldOffset(f); - } - - public static Object staticFieldBase(final Field f) { - return UNSAFE.staticFieldBase(f); - } - - public static boolean shouldBeInitialized(final Class c) { - return UNSAFE.shouldBeInitialized(c); - } - - public static void ensureClassInitialized(final Class c) { - UNSAFE.ensureClassInitialized(c); - } - - public static int arrayBaseOffset(final Class arrayClass) { - return UNSAFE.arrayBaseOffset(arrayClass); - } - - public static int arrayIndexScale(final Class arrayClass) { - return UNSAFE.arrayIndexScale(arrayClass); - } - - public static int addressSize() { - return UNSAFE.addressSize(); - } - - public static int pageSize() { - return UNSAFE.pageSize(); - } - -// public static Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, -// ProtectionDomain protectionDomain) { -// return UNSAFE.defineClass(name, b, off, len, loader, protectionDomain); -// MethodHandles.lookup(). -// } -// -// public static Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches) { -// return UNSAFE.defineAnonymousClass(hostClass, data, cpPatches); -// } - - public static Object allocateInstance(final Class cls) throws InstantiationException { - return UNSAFE.allocateInstance(cls); - } - - public static void throwException(final Throwable ee) { - UNSAFE.throwException(ee); - } - - public static boolean compareAndSwapObject(final Object o, final long offset, final Object expected, final Object x) { - return UNSAFE.compareAndSwapObject(o, offset, expected, x); - } - - public static boolean compareAndSwapInt(final Object o, final long offset, final int expected, final int x) { - return UNSAFE.compareAndSwapInt(o, offset, expected, x); - } - - public static boolean compareAndSwapLong(final Object o, final long offset, final long expected, final long x) { - return UNSAFE.compareAndSwapLong(o, offset, expected, x); - } - - public static Object getObjectVolatile(final Object o, final long offset) { - return UNSAFE.getObjectVolatile(o, offset); - } - - public static void putObjectVolatile(final Object o, final long offset, final Object x) { - UNSAFE.putObjectVolatile(o, offset, x); - } - - public static int getIntVolatile(final Object o, final long offset) { - return UNSAFE.getIntVolatile(o, offset); - } - - public static void putIntVolatile(final Object o, final long offset, final int x) { - UNSAFE.putIntVolatile(o, offset, x); - } - - public static boolean getBooleanVolatile(final Object o, final long offset) { - return UNSAFE.getBooleanVolatile(o, offset); - } - - public static void putBooleanVolatile(final Object o, final long offset, final boolean x) { - UNSAFE.putBooleanVolatile(o, offset, x); - } - - public static byte getByteVolatile(final Object o, final long offset) { - return UNSAFE.getByteVolatile(o, offset); - } - - public static void putByteVolatile(final Object o, final long offset, final byte x) { - UNSAFE.putByteVolatile(o, offset, x); - } - - public static short getShortVolatile(final Object o, final long offset) { - return UNSAFE.getShortVolatile(o, offset); - } - - public static void putShortVolatile(final Object o, final long offset, final short x) { - UNSAFE.putShortVolatile(o, offset, x); - } - - public static char getCharVolatile(final Object o, final long offset) { - return UNSAFE.getCharVolatile(o, offset); - } - - public static void putCharVolatile(final Object o, final long offset, final char x) { - UNSAFE.putCharVolatile(o, offset, x); - } - - public static long getLongVolatile(final Object o, final long offset) { - return UNSAFE.getLongVolatile(o, offset); - } - - public static void putLongVolatile(final Object o, final long offset, final long x) { - UNSAFE.putLongVolatile(o, offset, x); - } - - public static float getFloatVolatile(final Object o, final long offset) { - return UNSAFE.getFloatVolatile(o, offset); - } - - public static void putFloatVolatile(final Object o, final long offset, final float x) { - UNSAFE.putFloatVolatile(o, offset, x); - } - - public static double getDoubleVolatile(final Object o, final long offset) { - return UNSAFE.getDoubleVolatile(o, offset); - } - - public static void putDoubleVolatile(final Object o, final long offset, final double x) { - UNSAFE.putDoubleVolatile(o, offset, x); - } - - public static void putOrderedObject(final Object o, final long offset, final Object x) { - UNSAFE.putOrderedObject(o, offset, x); - } - - public static void putOrderedInt(final Object o, final long offset, final int x) { - UNSAFE.putOrderedInt(o, offset, x); - } - - public static void putOrderedLong(final Object o, final long offset, final long x) { - UNSAFE.putOrderedLong(o, offset, x); - } - - public static void unpark(final Object thread) { - UNSAFE.unpark(thread); - } - - public static void park(final boolean isAbsolute, final long time) { - UNSAFE.park(isAbsolute, time); - } - - public static int getLoadAverage(final double[] loadavg, final int nelems) { - return UNSAFE.getLoadAverage(loadavg, nelems); - } - - public static int getAndAddInt(final Object o, final long offset, final int delta) { - return UNSAFE.getAndAddInt(o, offset, delta); - } - - public static long getAndAddLong(final Object o, final long offset, final long delta) { - return UNSAFE.getAndAddLong(o, offset, delta); - } - - public static int getAndSetInt(final Object o, final long offset, final int newValue) { - return UNSAFE.getAndSetInt(o, offset, newValue); - } - - public static long getAndSetLong(final Object o, final long offset, final long newValue) { - return UNSAFE.getAndSetLong(o, offset, newValue); - } - - public static Object getAndSetObject(final Object o, final long offset, final Object newValue) { - return UNSAFE.getAndSetObject(o, offset, newValue); - } - - public static void loadFence() { - UNSAFE.loadFence(); - } - - public static void storeFence() { - UNSAFE.storeFence(); - } - - public static void fullFence() { - UNSAFE.fullFence(); - } - -} diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/UnsafeAccess.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/UnsafeAccess.java deleted file mode 100644 index 3902a9c0979..00000000000 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/hbase/UnsafeAccess.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copy of https://github.com/apache/hbase/blob/master/hbase-common/src/main/java/org/apache/hadoop/hbase/util/UnsafeAccess.java - * to avoid having to pull in all of hbase to use the util methods. - * - * 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 - * - * 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. - */ - -package stroom.bytebuffer.hbase; - -import org.apache.hbase.thirdparty.io.netty.util.internal.PlatformDependent; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public final class UnsafeAccess { - - /** - * The offset to the first element in a byte array. - */ - public static final long BYTE_ARRAY_BASE_OFFSET; - - public static final boolean LITTLE_ENDIAN = - ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); - - // This number limits the number of bytes to copy per call to Unsafe's - // copyMemory method. A limit is imposed to allow for safepoint polling - // during a large copy - static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L; - - static { - if (HBasePlatformDependent.isUnsafeAvailable()) { - BYTE_ARRAY_BASE_OFFSET = HBasePlatformDependent.arrayBaseOffset(byte[].class); - } else { - BYTE_ARRAY_BASE_OFFSET = -1; - } - } - - private UnsafeAccess() { - } - - // APIs to read primitive data from a byte[] using Unsafe way - - /** - * Converts a byte array to a short value considering it was written in big-endian format. - * - * @param bytes byte array - * @param offset offset into array - * @return the short value - */ - public static short toShort(final byte[] bytes, final int offset) { - if (LITTLE_ENDIAN) { - return Short - .reverseBytes(HBasePlatformDependent.getShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET)); - } else { - return HBasePlatformDependent.getShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET); - } - } - - /** - * Converts a byte array to an int value considering it was written in big-endian format. - * - * @param bytes byte array - * @param offset offset into array - * @return the int value - */ - public static int toInt(final byte[] bytes, final int offset) { - if (LITTLE_ENDIAN) { - return Integer - .reverseBytes(HBasePlatformDependent.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET)); - } else { - return HBasePlatformDependent.getInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET); - } - } - - /** - * Converts a byte array to a long value considering it was written in big-endian format. - * - * @param bytes byte array - * @param offset offset into array - * @return the long value - */ - public static long toLong(final byte[] bytes, final int offset) { - if (LITTLE_ENDIAN) { - return Long - .reverseBytes(HBasePlatformDependent.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET)); - } else { - return HBasePlatformDependent.getLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET); - } - } - - // APIs to write primitive data to a byte[] using Unsafe way - - /** - * Put a short value out to the specified byte array position in big-endian format. - * - * @param bytes the byte array - * @param offset position in the array - * @param val short to write out - * @return incremented offset - */ - public static int putShort(final byte[] bytes, final int offset, short val) { - if (LITTLE_ENDIAN) { - val = Short.reverseBytes(val); - } - HBasePlatformDependent.putShort(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val); - return offset + Bytes.SIZEOF_SHORT; - } - - /** - * Put an int value out to the specified byte array position in big-endian format. - * - * @param bytes the byte array - * @param offset position in the array - * @param val int to write out - * @return incremented offset - */ - public static int putInt(final byte[] bytes, final int offset, int val) { - if (LITTLE_ENDIAN) { - val = Integer.reverseBytes(val); - } - HBasePlatformDependent.putInt(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val); - return offset + Bytes.SIZEOF_INT; - } - - /** - * Put a long value out to the specified byte array position in big-endian format. - * - * @param bytes the byte array - * @param offset position in the array - * @param val long to write out - * @return incremented offset - */ - public static int putLong(final byte[] bytes, final int offset, long val) { - if (LITTLE_ENDIAN) { - val = Long.reverseBytes(val); - } - HBasePlatformDependent.putLong(bytes, offset + BYTE_ARRAY_BASE_OFFSET, val); - return offset + Bytes.SIZEOF_LONG; - } - - // APIs to read primitive data from a ByteBuffer using Unsafe way - - /** - * Reads a short value at the given buffer's offset considering it was written in big-endian - * format. - * - * @return short value at offset - */ - public static short toShort(final ByteBuffer buf, final int offset) { - if (LITTLE_ENDIAN) { - return Short.reverseBytes(getAsShort(buf, offset)); - } - return getAsShort(buf, offset); - } - - /** - * Reads a short value at the given Object's offset considering it was written in big-endian - * format. - * - * @return short value at offset - */ - public static short toShort(final Object ref, final long offset) { - if (LITTLE_ENDIAN) { - return Short.reverseBytes(HBasePlatformDependent.getShort(ref, offset)); - } - return HBasePlatformDependent.getShort(ref, offset); - } - - /** - * Reads bytes at the given offset as a short value. - * - * @return short value at offset - */ - private static short getAsShort(final ByteBuffer buf, final int offset) { - if (buf.isDirect()) { - return HBasePlatformDependent.getShort(directBufferAddress(buf) + offset); - } - return HBasePlatformDependent.getShort(buf.array(), - BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset); - } - - /** - * Reads an int value at the given buffer's offset considering it was written in big-endian - * format. - * - * @return int value at offset - */ - public static int toInt(final ByteBuffer buf, final int offset) { - if (LITTLE_ENDIAN) { - return Integer.reverseBytes(getAsInt(buf, offset)); - } - return getAsInt(buf, offset); - } - - /** - * Reads a int value at the given Object's offset considering it was written in big-endian format. - * - * @return int value at offset - */ - public static int toInt(final Object ref, final long offset) { - if (LITTLE_ENDIAN) { - return Integer.reverseBytes(HBasePlatformDependent.getInt(ref, offset)); - } - return HBasePlatformDependent.getInt(ref, offset); - } - - /** - * Reads bytes at the given offset as an int value. - * - * @return int value at offset - */ - private static int getAsInt(final ByteBuffer buf, final int offset) { - if (buf.isDirect()) { - return HBasePlatformDependent.getInt(directBufferAddress(buf) + offset); - } - return HBasePlatformDependent.getInt(buf.array(), - BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset); - } - - /** - * Reads a long value at the given buffer's offset considering it was written in big-endian - * format. - * - * @return long value at offset - */ - public static long toLong(final ByteBuffer buf, final int offset) { - if (LITTLE_ENDIAN) { - return Long.reverseBytes(getAsLong(buf, offset)); - } - return getAsLong(buf, offset); - } - - /** - * Reads a long value at the given Object's offset considering it was written in big-endian - * format. - * - * @return long value at offset - */ - public static long toLong(final Object ref, final long offset) { - if (LITTLE_ENDIAN) { - return Long.reverseBytes(HBasePlatformDependent.getLong(ref, offset)); - } - return HBasePlatformDependent.getLong(ref, offset); - } - - /** - * Reads bytes at the given offset as a long value. - * - * @return long value at offset - */ - private static long getAsLong(final ByteBuffer buf, final int offset) { - if (buf.isDirect()) { - return HBasePlatformDependent.getLong(directBufferAddress(buf) + offset); - } - return HBasePlatformDependent.getLong(buf.array(), - BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset); - } - - /** - * Returns the byte at the given offset - * - * @param buf the buffer to read - * @param offset the offset at which the byte has to be read - * @return the byte at the given offset - */ - public static byte toByte(final ByteBuffer buf, final int offset) { - if (buf.isDirect()) { - return HBasePlatformDependent.getByte(directBufferAddress(buf) + offset); - } else { - return HBasePlatformDependent.getByte(buf.array(), - BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset); - } - } - - /** - * Returns the byte at the given offset of the object - * - * @return the byte at the given offset - */ - public static byte toByte(final Object ref, final long offset) { - return HBasePlatformDependent.getByte(ref, offset); - } - - /** - * Put an int value out to the specified ByteBuffer offset in big-endian format. - * - * @param buf the ByteBuffer to write to - * @param offset offset in the ByteBuffer - * @param val int to write out - * @return incremented offset - */ - public static int putInt(final ByteBuffer buf, final int offset, int val) { - if (LITTLE_ENDIAN) { - val = Integer.reverseBytes(val); - } - if (buf.isDirect()) { - HBasePlatformDependent.putInt(directBufferAddress(buf) + offset, val); - } else { - HBasePlatformDependent.putInt(buf.array(), - offset + buf.arrayOffset() + BYTE_ARRAY_BASE_OFFSET, val); - } - return offset + Bytes.SIZEOF_INT; - } - - // APIs to copy data. This will be direct memory location copy and will be much faster - - /** - * Copies the bytes from given array's offset to length part into the given buffer. - * - * @param src source array - * @param srcOffset offset into source buffer - * @param dest destination buffer - * @param destOffset offset into destination buffer - * @param length length of data to copy - */ - public static void copy(final byte[] src, final int srcOffset, final ByteBuffer dest, final int destOffset, final int length) { - long destAddress = destOffset; - Object destBase = null; - if (dest.isDirect()) { - destAddress = destAddress + directBufferAddress(dest); - } else { - destAddress = destAddress + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset(); - destBase = dest.array(); - } - final long srcAddress = srcOffset + BYTE_ARRAY_BASE_OFFSET; - unsafeCopy(src, srcAddress, destBase, destAddress, length); - } - - private static void unsafeCopy(final Object src, long srcAddr, final Object dst, long destAddr, long len) { - while (len > 0) { - final long size = (len > UNSAFE_COPY_THRESHOLD) - ? UNSAFE_COPY_THRESHOLD - : len; - HBasePlatformDependent.copyMemory(src, srcAddr, dst, destAddr, size); - len -= size; - srcAddr += size; - destAddr += size; - } - } - - /** - * Copies specified number of bytes from given offset of {@code src} ByteBuffer to the - * {@code dest} array. - * - * @param src source buffer - * @param srcOffset offset into source buffer - * @param dest destination array - * @param destOffset offset into destination buffer - * @param length length of data to copy - */ - public static void copy(final ByteBuffer src, final int srcOffset, final byte[] dest, final int destOffset, final int length) { - long srcAddress = srcOffset; - Object srcBase = null; - if (src.isDirect()) { - srcAddress = srcAddress + directBufferAddress(src); - } else { - srcAddress = srcAddress + BYTE_ARRAY_BASE_OFFSET + src.arrayOffset(); - srcBase = src.array(); - } - final long destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET; - unsafeCopy(srcBase, srcAddress, dest, destAddress, length); - } - - /** - * Copies specified number of bytes from given offset of {@code src} buffer into the {@code dest} - * buffer. - * - * @param src source buffer - * @param srcOffset offset into source buffer - * @param dest destination buffer - * @param destOffset offset into destination buffer - * @param length length of data to copy - */ - public static void copy(final ByteBuffer src, final int srcOffset, final ByteBuffer dest, final int destOffset, - final int length) { - final long srcAddress; - final long destAddress; - Object srcBase = null, destBase = null; - if (src.isDirect()) { - srcAddress = srcOffset + directBufferAddress(src); - } else { - srcAddress = (long) srcOffset + src.arrayOffset() + BYTE_ARRAY_BASE_OFFSET; - srcBase = src.array(); - } - if (dest.isDirect()) { - destAddress = destOffset + directBufferAddress(dest); - } else { - destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset(); - destBase = dest.array(); - } - unsafeCopy(srcBase, srcAddress, destBase, destAddress, length); - } - - // APIs to add primitives to BBs - - /** - * Put a short value out to the specified BB position in big-endian format. - * - * @param buf the byte buffer - * @param offset position in the buffer - * @param val short to write out - * @return incremented offset - */ - public static int putShort(final ByteBuffer buf, final int offset, short val) { - if (LITTLE_ENDIAN) { - val = Short.reverseBytes(val); - } - if (buf.isDirect()) { - HBasePlatformDependent.putShort(directBufferAddress(buf) + offset, val); - } else { - HBasePlatformDependent.putShort(buf.array(), - BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val); - } - return offset + Bytes.SIZEOF_SHORT; - } - - /** - * Put a long value out to the specified BB position in big-endian format. - * - * @param buf the byte buffer - * @param offset position in the buffer - * @param val long to write out - * @return incremented offset - */ - public static int putLong(final ByteBuffer buf, final int offset, long val) { - if (LITTLE_ENDIAN) { - val = Long.reverseBytes(val); - } - if (buf.isDirect()) { - HBasePlatformDependent.putLong(directBufferAddress(buf) + offset, val); - } else { - HBasePlatformDependent.putLong(buf.array(), - BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, val); - } - return offset + Bytes.SIZEOF_LONG; - } - - /** - * Put a byte value out to the specified BB position in big-endian format. - * - * @param buf the byte buffer - * @param offset position in the buffer - * @param b byte to write out - * @return incremented offset - */ - public static int putByte(final ByteBuffer buf, final int offset, final byte b) { - if (buf.isDirect()) { - HBasePlatformDependent.putByte(directBufferAddress(buf) + offset, b); - } else { - HBasePlatformDependent.putByte(buf.array(), - BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset, b); - } - return offset + 1; - } - - public static long directBufferAddress(final ByteBuffer buf) { - return PlatformDependent.directBufferAddress(buf); - } - - public static void freeDirectBuffer(final ByteBuffer buffer) { - // here we just use the method in netty - PlatformDependent.freeDirectBuffer(buffer); - } -} diff --git a/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestByteBufferUtils.java b/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestByteBufferUtils.java index 5c62495c2fb..4b57feb3ebf 100644 --- a/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestByteBufferUtils.java +++ b/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestByteBufferUtils.java @@ -394,8 +394,7 @@ void testCopyPerformance() { src.flip(); final Map> funcMap = Map.of( - "Simple", this::doSimpleCopyTest, - "Hadoop", this::doHadoopCopyTest); + "Simple", this::doSimpleCopyTest); for (int j = 0; j < rounds; j++) { final int round = j; @@ -422,10 +421,6 @@ void testCopyPerformance() { } } - private void doHadoopCopyTest(final ByteBuffer src, final ByteBuffer dest) { - stroom.bytebuffer.hbase.ByteBufferUtils.copyFromBufferToBuffer(src, dest); - } - private void doSimpleCopyTest(final ByteBuffer src, final ByteBuffer dest) { dest.put(src); } diff --git a/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java b/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java index 8fff29fc41b..1ee6598c97e 100644 --- a/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java +++ b/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java @@ -308,10 +308,7 @@ void testWrite_byteBuffer() throws IOException { .isGreaterThan(BYTES); assertThat(pooledBuffer.limit()) .isEqualTo(BYTES); - assertThat(ByteBufferUtils.compareTo( - byteBuffer, 5, BYTES, - pooledBuffer, 0, BYTES)) - .isZero(); + assertThat(byteBuffer.slice(5, BYTES)).isEqualTo(pooledBuffer.slice(0, BYTES)); } } } diff --git a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/databases/ValueStoreDb.java b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/databases/ValueStoreDb.java index bf2415e9f7e..56c0e74c84c 100644 --- a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/databases/ValueStoreDb.java +++ b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/databases/ValueStoreDb.java @@ -214,7 +214,7 @@ public ByteBuffer getOrCreateKey(final Txn writeTxn, short lastKeyId = -1; while (isFound) { - if (ValueStoreKeySerde.compareValueHashCode(startKey, cursor.key()) != 0) { + if (!ValueStoreKeySerde.valueHashCodeEquals(startKey, cursor.key())) { // cursor key has a different hashcode to ours so we can stop looping break; } diff --git a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/StagingRefDataValueSerde.java b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/StagingRefDataValueSerde.java index fc697b6e642..f85ec95f3f5 100644 --- a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/StagingRefDataValueSerde.java +++ b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/StagingRefDataValueSerde.java @@ -1,7 +1,6 @@ package stroom.pipeline.refdata.store.offheapstore.serdes; import stroom.bytebuffer.PooledByteBufferOutputStream; -import stroom.bytebuffer.hbase.Bytes; import stroom.lmdb.serde.Deserializer; import stroom.lmdb.serde.Serde; import stroom.lmdb.serde.Serializer; @@ -68,7 +67,7 @@ public ByteBuffer serialize(final PooledByteBufferOutputStream pooledByteBufferO try { pooledByteBufferOutputStream.write(stagingRefDataValue.getTypeId()); - pooledByteBufferOutputStream.write(Bytes.toBytes(refDataValue.getValueHashCode(valueStoreHashAlgorithm))); + pooledByteBufferOutputStream.writeLong(refDataValue.getValueHashCode(valueStoreHashAlgorithm)); final ByteBuffer refDataValueBuffer = genericRefDataValueSerde.serialize( pooledByteBufferOutputStream, refDataValue); diff --git a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreKeySerde.java b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreKeySerde.java index 2fb6cfad63f..1e7ff4ad7b3 100644 --- a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreKeySerde.java +++ b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreKeySerde.java @@ -23,6 +23,7 @@ import stroom.util.logging.LogUtil; import java.nio.ByteBuffer; +import java.util.Objects; public class ValueStoreKeySerde implements Serde { @@ -88,17 +89,19 @@ public static short extractId(final ByteBuffer byteBuffer) { } /** - * Compare the valueHashCode part of both byte buffers, comparing in byte form + * Check equality of valueHashCode part of both byte buffers */ - public static int compareValueHashCode(final ByteBuffer thisBuffer, final ByteBuffer thatBuffer) { + public static boolean valueHashCodeEquals(final ByteBuffer thisBuffer, final ByteBuffer thatBuffer) { try { - return ByteBufferUtils.compareTo( - thisBuffer, VALUE_HASH_CODE_OFFSET, VALUE_HASH_CODE_BYTES, - thatBuffer, VALUE_HASH_CODE_OFFSET, VALUE_HASH_CODE_BYTES); + return Objects.equals(getValueHashCodeSlice(thisBuffer), getValueHashCodeSlice(thatBuffer)); } catch (final Exception e) { - throw new RuntimeException(LogUtil.message("Error comparing [{}] & [{}]", + throw new RuntimeException(LogUtil.message("Error checking equality [{}] & [{}]", ByteBufferUtils.byteBufferInfo(thisBuffer), ByteBufferUtils.byteBufferInfo(thatBuffer)), e); } } + + private static ByteBuffer getValueHashCodeSlice(final ByteBuffer byteBuffer) { + return byteBuffer.slice(VALUE_HASH_CODE_OFFSET, VALUE_HASH_CODE_BYTES); + } } diff --git a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java index 846e50186cc..b8a6c79239c 100644 --- a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java +++ b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java @@ -25,16 +25,24 @@ public class ValueStoreMetaSerde implements Serde { private static final Logger LOGGER = LoggerFactory.getLogger(ValueStoreMetaSerde.class); private static final UnsignedBytes REF_COUNT_UNSIGNED_BYTES = UnsignedBytesInstances.THREE; - private static final byte[] REF_COUNT_ZERO = REF_COUNT_UNSIGNED_BYTES.toBytes(0); - private static final byte[] REF_COUNT_ONE = REF_COUNT_UNSIGNED_BYTES.toBytes(1); + private static final int REFERENCE_COUNT_BYTES = REF_COUNT_UNSIGNED_BYTES.length(); private static final int TYPE_ID_OFFSET = 0; private static final int TYPE_ID_BYTES = 1; private static final int REFERENCE_COUNT_OFFSET = TYPE_ID_OFFSET + TYPE_ID_BYTES; - private static final int REFERENCE_COUNT_BYTES = REF_COUNT_UNSIGNED_BYTES.length(); private static final int BUFFER_CAPACITY = TYPE_ID_BYTES + REFERENCE_COUNT_BYTES; + private static final ByteBuffer ZERO = ByteBuffer.allocateDirect(REFERENCE_COUNT_BYTES); + private static final ByteBuffer ONE = ByteBuffer.allocateDirect(REFERENCE_COUNT_BYTES); + + static { + REF_COUNT_UNSIGNED_BYTES.put(ZERO, 0); + ZERO.flip(); + REF_COUNT_UNSIGNED_BYTES.put(ONE, 1); + ONE.flip(); + } + @Override public int getBufferCapacity() { return BUFFER_CAPACITY; @@ -53,8 +61,7 @@ public ValueStoreMeta deserialize(final ByteBuffer byteBuffer) { @Override public void serialize(final ByteBuffer byteBuffer, final ValueStoreMeta valueStoreMeta) { - - byteBuffer.put((byte) valueStoreMeta.getTypeId()); + byteBuffer.put(valueStoreMeta.getTypeId()); REF_COUNT_UNSIGNED_BYTES.put(byteBuffer, valueStoreMeta.getReferenceCount()); byteBuffer.flip(); } @@ -77,15 +84,8 @@ public int extractReferenceCount(final ByteBuffer byteBuffer) { * @return True if the reference count is one or zero. */ public boolean isLastReference(final ByteBuffer byteBuffer) { - // Ever so slightly cheaper than extracting the count and checking the long value - // TODO could maybe use ByteBufferUtils.equals - return stroom.bytebuffer.hbase.ByteBufferUtils.compareTo( - REF_COUNT_ONE, 0, REFERENCE_COUNT_BYTES, - byteBuffer, REFERENCE_COUNT_OFFSET, REFERENCE_COUNT_BYTES) == 0L - || - stroom.bytebuffer.hbase.ByteBufferUtils.compareTo( - REF_COUNT_ZERO, 0, REFERENCE_COUNT_BYTES, - byteBuffer, REFERENCE_COUNT_OFFSET, REFERENCE_COUNT_BYTES) == 0L; + final ByteBuffer slice = byteBuffer.slice(REFERENCE_COUNT_OFFSET, REFERENCE_COUNT_BYTES); + return ONE.equals(slice) || ZERO.equals(slice); } public void cloneAndDecrementRefCount(final ByteBuffer sourceBuffer, final ByteBuffer destBuffer) { diff --git a/stroom-state/stroom-state-impl/build.gradle b/stroom-state/stroom-state-impl/build.gradle index 72bbf323f4b..2035b0d4727 100644 --- a/stroom-state/stroom-state-impl/build.gradle +++ b/stroom-state/stroom-state-impl/build.gradle @@ -39,3 +39,7 @@ dependencies { testImplementation libs.bundles.common.test.implementation testRuntimeOnly libs.bundles.common.test.runtime } + +tasks.withType(AbstractTestTask).configureEach { + failOnNoDiscoveredTests = false +} diff --git a/stroom-statistics/stroom-statistics-impl-hbase/src/main/java/stroom/statistics/impl/hbase/rollup/RollUpBitMask.java b/stroom-statistics/stroom-statistics-impl-hbase/src/main/java/stroom/statistics/impl/hbase/rollup/RollUpBitMask.java index 4285f40b41b..a3f42b6a76d 100644 --- a/stroom-statistics/stroom-statistics-impl-hbase/src/main/java/stroom/statistics/impl/hbase/rollup/RollUpBitMask.java +++ b/stroom-statistics/stroom-statistics-impl-hbase/src/main/java/stroom/statistics/impl/hbase/rollup/RollUpBitMask.java @@ -16,8 +16,6 @@ package stroom.statistics.impl.hbase.rollup; -import stroom.bytebuffer.hbase.Bytes; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -66,11 +64,9 @@ public class RollUpBitMask { } private final short mask; - private final byte[] maskAsBytes; private RollUpBitMask(final short mask) { this.mask = mask; - this.maskAsBytes = Bytes.toBytes(mask); } /** @@ -140,28 +136,6 @@ public static RollUpBitMask fromRolledupTagList(final String allTags, final Stri return fromTagPositions(rolledUpTagPositions); } - /** - * @param allTags String containing all tags in any order delimited by comma, - * e.g. "tag1,tag3,tag2" - * @param rolledUpTags String containing just those tags that are to be rolled up in - * any order e.g. "tag3,tag2" - * @return The byte value of the mask - */ - public static byte[] byteValueFromTagList(final String allTags, final String rolledUpTags) { - return fromRolledupTagList(allTags, rolledUpTags).asBytes(); - } - - /** - * @param allTags String containing all tags in any order delimited by comma, - * e.g. "tag1,tag3,tag2" - * @param rolledUpTags String containing just those tags that are to be rolled up in - * any order e.g. "tag3,tag2" - * @return The int value of the mask - */ - public static int intValueFromTagList(final String allTags, final String rolledUpTags) { - return fromRolledupTagList(allTags, rolledUpTags).asShort(); - } - /** * Generates a {@link RollUpBitMask} from the passed list of rolled up tag * positions. It uses a cache of tag positions to save it having to build @@ -261,16 +235,6 @@ private static short setMaskValueAtPosition(final int position, final short exis } - /** - * Constructor - * - * @param bytes The byte array to convert from - * @return A {@link RollUpBitMask} object built from the byte array - */ - public static RollUpBitMask fromBytes(final byte[] bytes) { - return new RollUpBitMask(Bytes.toShort(bytes)); - } - /** * @param tagCount The number of tags you want permutations for * @return A set of {@link RollUpBitMask} objects, one for each permutation @@ -529,17 +493,6 @@ public RollUpBitMask convert(final Map newToOldFieldPositionMa return RollUpBitMask.fromTagPositions(new TreeSet<>(rolledUpFieldPositions)); } - public short asShort() { - return this.mask; - } - - /** - * @return The mask as a byte array - */ - public byte[] asBytes() { - return this.maskAsBytes; - } - /** * Output has mask position zero on the right */ @@ -550,29 +503,16 @@ public String toString() { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + mask; - result = prime * result + Arrays.hashCode(maskAsBytes); - return result; + public boolean equals(final Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + final RollUpBitMask that = (RollUpBitMask) o; + return mask == that.mask; } @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final RollUpBitMask other = (RollUpBitMask) obj; - if (mask != other.mask) { - return false; - } - return Arrays.equals(maskAsBytes, other.maskAsBytes); + public int hashCode() { + return Objects.hashCode(mask); } } diff --git a/stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/ByteArrayUtils.java b/stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/ByteArrayUtils.java deleted file mode 100644 index 2362964ca13..00000000000 --- a/stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/ByteArrayUtils.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2016 Crown Copyright - * - * 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. - */ - -package stroom.statistics.impl.sql.rollup; - -import stroom.bytebuffer.hbase.Bytes; - -import jakarta.xml.bind.DatatypeConverter; - -public class ByteArrayUtils { - - /** - * Private constructor to prevent instantiation - */ - private ByteArrayUtils() { - // Do nothing, should never be called. - } - - /** - * Returns a string representation of a byte array - * - * @param arr The byte array - * @return A space delimited series of byte values - */ - public static String byteArrayToString(final byte[] arr) { - final StringBuilder sb = new StringBuilder(); - for (final byte b : arr) { - sb.append(b); - sb.append(" "); - } - return sb.toString().replaceAll(" $", ""); - } - - /** - * Converts a byte array into a hex representation with a space between each - * byte e.g 00 00 01 00 05 59 B3 - * - * @param arr The byte array to convert - * @return The byte array as a string of hex values separated by a spaces - */ - public static String byteArrayToHex(final byte[] arr) { - final StringBuilder sb = new StringBuilder(); - if (arr != null) { - for (final byte b : arr) { - final byte[] oneByteArr = new byte[1]; - oneByteArr[0] = b; - sb.append(DatatypeConverter.printHexBinary(oneByteArr)); - sb.append(" "); - } - } - return sb.toString().replaceAll(" $", ""); - } - - /** - * @return The array represented in hex, decimal and 'hbase' forms. The - * hbase form is mix of ascii and deciaml, so an ascii char if the - * byte value exists in the ascii table - */ - public static String byteArrayToAllForms(final byte[] arr) { - return ByteArrayUtils.byteArrayToHex(arr) + " (hex) | " + ByteArrayUtils.byteArrayToString(arr) + " (dec) | " - + Bytes.toStringBinary(arr) + " (hbase)"; - } -} diff --git a/stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/RollUpBitMask.java b/stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/RollUpBitMask.java index 8a1aa98003b..fdd19a4177f 100644 --- a/stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/RollUpBitMask.java +++ b/stroom-statistics/stroom-statistics-impl-sql/src/main/java/stroom/statistics/impl/sql/rollup/RollUpBitMask.java @@ -16,10 +16,9 @@ package stroom.statistics.impl.sql.rollup; -import stroom.bytebuffer.hbase.Bytes; - import jakarta.xml.bind.DatatypeConverter; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -68,11 +67,17 @@ public class RollUpBitMask { } private final short mask; - private final byte[] maskAsBytes; private RollUpBitMask(final short mask) { this.mask = mask; - this.maskAsBytes = Bytes.toBytes(mask); + } + + public static byte[] shortToBytes(final short s) { + return ByteBuffer.allocate(Short.BYTES).putShort(s).flip().array(); + } + + public static short bytesToShort(final byte[] bytes) { + return ByteBuffer.wrap(bytes).getShort(); } /** @@ -270,7 +275,7 @@ private static short setMaskValueAtPosition(final int position, final short exis * @return A {@link RollUpBitMask} object built from the byte array */ public static RollUpBitMask fromBytes(final byte[] bytes) { - return new RollUpBitMask(Bytes.toShort(bytes)); + return new RollUpBitMask(bytesToShort(bytes)); } /** @@ -539,11 +544,11 @@ public short asShort() { * @return The mask as a byte array */ public byte[] asBytes() { - return this.maskAsBytes; + return shortToBytes(mask); } public String asHexString() { - return DatatypeConverter.printHexBinary(this.maskAsBytes); + return DatatypeConverter.printHexBinary(asBytes()); } /** @@ -556,29 +561,16 @@ public String toString() { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + mask; - result = prime * result + Arrays.hashCode(maskAsBytes); - return result; + public boolean equals(final Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + final RollUpBitMask that = (RollUpBitMask) o; + return mask == that.mask; } @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final RollUpBitMask other = (RollUpBitMask) obj; - if (mask != other.mask) { - return false; - } - return Arrays.equals(maskAsBytes, other.maskAsBytes); + public int hashCode() { + return Objects.hashCode(mask); } } diff --git a/stroom-statistics/stroom-statistics-impl-sql/src/test/java/stroom/statistics/impl/sql/rollup/TestRollUpBitMask.java b/stroom-statistics/stroom-statistics-impl-sql/src/test/java/stroom/statistics/impl/sql/rollup/TestRollUpBitMask.java index 95d4bc3da8a..8c2ad66f3a9 100644 --- a/stroom-statistics/stroom-statistics-impl-sql/src/test/java/stroom/statistics/impl/sql/rollup/TestRollUpBitMask.java +++ b/stroom-statistics/stroom-statistics-impl-sql/src/test/java/stroom/statistics/impl/sql/rollup/TestRollUpBitMask.java @@ -17,6 +17,7 @@ package stroom.statistics.impl.sql.rollup; +import stroom.bytebuffer.ByteArrayUtils; import stroom.test.common.util.test.StroomUnitTest; import jakarta.xml.bind.DatatypeConverter; From a5471157bacaa971ec96f9d8673970a617e7d4ec Mon Sep 17 00:00:00 2001 From: stroomdev66 Date: Tue, 30 Sep 2025 13:57:08 +0100 Subject: [PATCH 3/7] Remove uses of unsafe --- .../TestPooledByteBufferOutputStream.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java b/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java index 1ee6598c97e..0b1133ca45e 100644 --- a/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java +++ b/stroom-bytebuffer/src/test/java/stroom/bytebuffer/TestPooledByteBufferOutputStream.java @@ -311,4 +311,28 @@ void testWrite_byteBuffer() throws IOException { assertThat(byteBuffer.slice(5, BYTES)).isEqualTo(pooledBuffer.slice(0, BYTES)); } } + + @Test + void testWriteLong() throws IOException { + final ByteBufferPool byteBufferPool = getByteBufferPool(); + try (final PooledByteBufferOutputStream pooledByteBufferOutputStream = new PooledByteBufferOutputStream( + byteBufferPool, + BYTES)) { + pooledByteBufferOutputStream.writeLong(234556L); + final ByteBuffer pooledBuffer = pooledByteBufferOutputStream.getByteBuffer(); + final byte[] actual = ByteBufferUtils.toBytes(pooledBuffer); + final byte[] expected = new byte[BYTES]; + oldPutLong(expected, 0, 234556L); + assertThat(actual).isEqualTo(expected); + } + } + + private int oldPutLong(final byte[] bytes, final int offset, long val) { + for (int i = offset + 7; i > offset; i--) { + bytes[i] = (byte) val; + val >>>= 8; + } + bytes[offset] = (byte) val; + return offset + BYTES; + } } From 34071a20e4f0d8518ba6a67aafa1948baa1568e5 Mon Sep 17 00:00:00 2001 From: at055612 <22818309+at055612@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:24:12 +0100 Subject: [PATCH 4/7] Make ValueStoreMetaSerde.isLastReference faster --- .../serdes/ValueStoreMetaSerde.java | 34 ++++++++++++------- .../serdes/TestValueStoreMetaSerde.java | 8 ++++- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java index b8a6c79239c..a3ba1f63f33 100644 --- a/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java +++ b/stroom-pipeline/src/main/java/stroom/pipeline/refdata/store/offheapstore/serdes/ValueStoreMetaSerde.java @@ -29,20 +29,17 @@ public class ValueStoreMetaSerde implements Serde { private static final int TYPE_ID_OFFSET = 0; private static final int TYPE_ID_BYTES = 1; + /** + * The offset of the first byte of the reference count + */ private static final int REFERENCE_COUNT_OFFSET = TYPE_ID_OFFSET + TYPE_ID_BYTES; + /** + * The offset of the last byte of the reference count + */ + public static final int REFERENCE_COUNT_END_OFFSET = REFERENCE_COUNT_OFFSET + REFERENCE_COUNT_BYTES - 1; private static final int BUFFER_CAPACITY = TYPE_ID_BYTES + REFERENCE_COUNT_BYTES; - private static final ByteBuffer ZERO = ByteBuffer.allocateDirect(REFERENCE_COUNT_BYTES); - private static final ByteBuffer ONE = ByteBuffer.allocateDirect(REFERENCE_COUNT_BYTES); - - static { - REF_COUNT_UNSIGNED_BYTES.put(ZERO, 0); - ZERO.flip(); - REF_COUNT_UNSIGNED_BYTES.put(ONE, 1); - ONE.flip(); - } - @Override public int getBufferCapacity() { return BUFFER_CAPACITY; @@ -84,8 +81,21 @@ public int extractReferenceCount(final ByteBuffer byteBuffer) { * @return True if the reference count is one or zero. */ public boolean isLastReference(final ByteBuffer byteBuffer) { - final ByteBuffer slice = byteBuffer.slice(REFERENCE_COUNT_OFFSET, REFERENCE_COUNT_BYTES); - return ONE.equals(slice) || ZERO.equals(slice); + // This relies on UnsignedBytes serialising 1 to 001 and 0 to 000. + + // Check the last byte first as low numbers are more likely than high numbers. + final byte lastByte = byteBuffer.get(REFERENCE_COUNT_END_OFFSET); + if (lastByte != 1 && lastByte != 0) { + return false; + } + + // Everything else should be zero + for (int j = REFERENCE_COUNT_END_OFFSET - 1; j >= REFERENCE_COUNT_OFFSET; j--) { + if (byteBuffer.get(j) != 0) { + return false; + } + } + return true; } public void cloneAndDecrementRefCount(final ByteBuffer sourceBuffer, final ByteBuffer destBuffer) { diff --git a/stroom-pipeline/src/test/java/stroom/pipeline/refdata/store/offheapstore/serdes/TestValueStoreMetaSerde.java b/stroom-pipeline/src/test/java/stroom/pipeline/refdata/store/offheapstore/serdes/TestValueStoreMetaSerde.java index 14c789bcb67..57bb44293ea 100644 --- a/stroom-pipeline/src/test/java/stroom/pipeline/refdata/store/offheapstore/serdes/TestValueStoreMetaSerde.java +++ b/stroom-pipeline/src/test/java/stroom/pipeline/refdata/store/offheapstore/serdes/TestValueStoreMetaSerde.java @@ -85,6 +85,11 @@ Stream testIsLastReference() { .addCase(1, true) .addCase(2, false) .addCase(3, false) + .addCase(8, false) + .addCase(10, false) + .addCase(100, false) + .addCase(1000, false) + .addCase(10000, false) .addCase(16_000_000, false) .build(); } @@ -174,6 +179,7 @@ void testDecrementRefCount() { @Override TypeLiteral getSerdeType() { - return new TypeLiteral(){}; + return new TypeLiteral() { + }; } } From 52a6347cc7ed820e0a5f806367cab4d27a1ce54c Mon Sep 17 00:00:00 2001 From: stroomdev66 Date: Wed, 1 Oct 2025 11:35:47 +0100 Subject: [PATCH 5/7] Add equals method for byte buffers --- .../java/stroom/bytebuffer/ByteBufferUtils.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java index 5e8beb1abbc..48c1c4964eb 100644 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java +++ b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java @@ -453,4 +453,20 @@ public static void padMax(final ByteBuffer byteBuffer, final int offset, final i byteBuffer.put(i, MAX_BYTE_UNSIGNED); } } + + public static boolean equals(final ByteBuffer a, + final int aOff, + final ByteBuffer b, + final int bOff, + final int length) { + if (length > 7) { + return a.slice(aOff, length).equals(b.slice(bOff, length)); + } + for (int i = 0; i < length; i++) { + if (a.get(aOff + i) != b.get(bOff + i)) { + return false; + } + } + return true; + } } From 72ab85256c4c6c82f502bd615dab84bfe4390b31 Mon Sep 17 00:00:00 2001 From: stroomdev66 Date: Wed, 1 Oct 2025 11:47:45 +0100 Subject: [PATCH 6/7] Add equals method for byte buffers --- .../main/java/stroom/bytebuffer/ByteBufferUtils.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java index 48c1c4964eb..429d25ab695 100644 --- a/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java +++ b/stroom-bytebuffer/src/main/java/stroom/bytebuffer/ByteBufferUtils.java @@ -454,6 +454,17 @@ public static void padMax(final ByteBuffer byteBuffer, final int offset, final i } } + /** + * Check for byte buffer equality over portions of two buffers. This is generally quicker than slicing as no object + * creation is required. + * + * @param a Byte buffer 1. + * @param aOff Byte buffer 1 offset. + * @param b Byte buffer 2. + * @param bOff Byte buffer 2 offset. + * @param length Length to compare. + * @return True if byte buffer portions are equal. + */ public static boolean equals(final ByteBuffer a, final int aOff, final ByteBuffer b, From 35f835112d4f9ff9d81c14b0bf49e80fc5b1aab1 Mon Sep 17 00:00:00 2001 From: at055612 <22818309+at055612@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:40:03 +0100 Subject: [PATCH 7/7] Update checkout and codql GH actions (cherry picked from commit 18968a09897cda80495b713940e10a26ede1a707) --- .github/workflows/codeql-analysis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6b8a99169c0..2f0ce071751 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -4,7 +4,7 @@ on: push: branches: - "master" - - "[0-9].[0-9]" + - "[0-9]+.[0-9]+" #pull_request: ## The branches below must be a subset of the branches above #branches: @@ -32,14 +32,14 @@ jobs: steps: - name: Install Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v5 with: distribution: 'temurin' # See 'Supported distributions' for available options #distribution: 'zulu' # See 'Supported distributions' for available options - java-version: '21' + java-version: '25' - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL @@ -83,4 +83,4 @@ jobs: -x gwtCompile - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3