diff --git a/ci/run_ci.sh b/ci/run_ci.sh index 45e096a45e..fafe3185de 100755 --- a/ci/run_ci.sh +++ b/ci/run_ci.sh @@ -98,6 +98,7 @@ integration_tests() { echo "Start JPMS tests" cd "$ROOT"/integration_tests/jpms_tests mvn -T10 -B --no-transfer-progress clean compile + ./run_jlink_smoke.sh echo "Start jdk compatibility tests" cd "$ROOT"/integration_tests/jdk_compatibility_tests mvn -T10 -B --no-transfer-progress clean test diff --git a/integration_tests/jpms_tests/run_jlink_smoke.sh b/integration_tests/jpms_tests/run_jlink_smoke.sh new file mode 100755 index 0000000000..08364dc486 --- /dev/null +++ b/integration_tests/jpms_tests/run_jlink_smoke.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash + +# 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. + +set -euo pipefail + +ROOT="$(git rev-parse --show-toplevel)" +JAVA_ROOT="$ROOT/java" +VERSION="$(mvn -q -B -f "$JAVA_ROOT/pom.xml" help:evaluate -Dexpression=project.version -DforceStdout)" +JAVA_HOME="${JAVA_HOME:-$( + java -XshowSettings:properties -version 2>&1 \ + | awk -F= '/java.home =/ { gsub(/^ +| +$/, "", $2); print $2; exit }' +)}" +JAVA_MAJOR="$( + java -version 2>&1 \ + | awk -F '"' '/version/ {print $2; exit}' \ + | awk -F. '{ if ($1 == "1") print $2; else print $1 }' +)" + +if [[ "$JAVA_MAJOR" -lt 11 ]]; then + echo "Skipping jlink smoke test on JDK $JAVA_MAJOR; fory-format is Java 11+." + exit 0 +fi +if [[ ! -d "$JAVA_HOME/jmods" ]]; then + echo "Cannot find JDK modules under JAVA_HOME=$JAVA_HOME; jlink smoke requires a JDK." >&2 + exit 1 +fi + +artifact_jar() { + local artifact="$1" + local module="$2" + local target_jar="$JAVA_ROOT/$module/target/$artifact-$VERSION.jar" + local repo_jar="$HOME/.m2/repository/org/apache/fory/$artifact/$VERSION/$artifact-$VERSION.jar" + if [[ -f "$target_jar" ]]; then + echo "$target_jar" + elif [[ -f "$repo_jar" ]]; then + echo "$repo_jar" + else + echo "Cannot find $artifact jar; run java/fory-core and java/fory-format package/install first." >&2 + exit 1 + fi +} + +CORE_JAR="$(artifact_jar fory-core fory-core)" +FORMAT_JAR="$(artifact_jar fory-format fory-format)" +WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/fory-jpms-jlink.XXXXXX")" +trap 'rm -rf "$WORK_DIR"' EXIT + +require_jar_entry() { + local jar_file="$1" + local entry="$2" + if ! jar tf "$jar_file" | grep -qx "$entry"; then + echo "Missing $entry in $jar_file" >&2 + exit 1 + fi +} + +reject_jar_entry() { + local jar_file="$1" + local entry="$2" + if jar tf "$jar_file" | grep -qx "$entry"; then + echo "Unexpected root $entry in $jar_file" >&2 + exit 1 + fi +} + +require_jar_entry "$CORE_JAR" "META-INF/versions/9/module-info.class" +reject_jar_entry "$CORE_JAR" "module-info.class" +require_jar_entry "$FORMAT_JAR" "META-INF/versions/11/module-info.class" +reject_jar_entry "$FORMAT_JAR" "module-info.class" + +if [[ "$JAVA_MAJOR" -ge 16 ]] \ + && jar tf "$CORE_JAR" | grep -qx "META-INF/versions/16/module-info.class"; then + require_jar_entry "$CORE_JAR" "META-INF/versions/16/org/apache/fory/serializer/CompressedArraySerializers.class" + require_jar_entry "$CORE_JAR" "META-INF/versions/16/org/apache/fory/util/ArrayCompressionUtils.class" + require_jar_entry "$CORE_JAR" "META-INF/versions/16/org/apache/fory/util/PrimitiveArrayCompressionType.class" + reject_jar_entry "$CORE_JAR" "org/apache/fory/serializer/CompressedArraySerializers.class" + reject_jar_entry "$CORE_JAR" "org/apache/fory/util/ArrayCompressionUtils.class" + reject_jar_entry "$CORE_JAR" "org/apache/fory/util/PrimitiveArrayCompressionType.class" + jar --file "$CORE_JAR" --describe-module --release 16 | grep -q "requires jdk.incubator.vector static" +fi + +jar --file "$CORE_JAR" --describe-module --release 9 | grep -q "requires java.sql static" +jar --file "$CORE_JAR" --describe-module --release 9 | grep -q "requires com.google.common static" +jar --file "$FORMAT_JAR" --describe-module --release 11 | grep -q "requires java.sql static" +jar --file "$FORMAT_JAR" --describe-module --release 11 | grep -q "requires org.apache.arrow.vector static transitive" +jar --file "$FORMAT_JAR" --describe-module --release 11 | grep -q "requires org.apache.arrow.memory.core static transitive" + +DEPS_FILE="$WORK_DIR/format-deps.txt" +mvn -q -B -f "$JAVA_ROOT/pom.xml" -pl fory-format dependency:list \ + -DincludeScope=compile \ + -DoutputAbsoluteArtifactFilename=true \ + -DexcludeTransitive=false \ + -DoutputFile="$DEPS_FILE" \ + -Dstyle.color=never + +COMPILE_MODULE_PATH="$WORK_DIR/compile-module-path.txt" +awk -F: '/:jar:/ { + path = $NF + sub(/ .*/, "", path) + if (path ~ /^\// && path ~ /\.jar$/ && path !~ /\/org\/apache\/fory\/fory-core\//) { + print path + } +}' "$DEPS_FILE" > "$COMPILE_MODULE_PATH" + +mkdir -p "$WORK_DIR/src/jpms.smoke/org/apache/fory/jpms" "$WORK_DIR/mods" +cat > "$WORK_DIR/src/jpms.smoke/module-info.java" <<'EOF' +module jpms.smoke { + requires org.apache.fory.core; + requires org.apache.fory.format; +} +EOF +cat > "$WORK_DIR/src/jpms.smoke/org/apache/fory/jpms/Smoke.java" <<'EOF' +package org.apache.fory.jpms; + +import org.apache.fory.Fory; + +public final class Smoke { + public static void main(String[] args) throws Exception { + Fory.builder().build(); + Class.forName("org.apache.fory.format.encoder.Encoders"); + System.out.println("ok"); + } +} +EOF + +JOINED_COMPILE_MODULE_PATH="$CORE_JAR:$FORMAT_JAR" +if [[ -s "$COMPILE_MODULE_PATH" ]]; then + JOINED_COMPILE_MODULE_PATH="$JOINED_COMPILE_MODULE_PATH:$(paste -sd: "$COMPILE_MODULE_PATH")" +fi + +javac \ + --module-path "$JOINED_COMPILE_MODULE_PATH" \ + -d "$WORK_DIR/mods" \ + --module-source-path "$WORK_DIR/src" \ + -m jpms.smoke + +jlink \ + --module-path "$JAVA_HOME/jmods:$CORE_JAR:$FORMAT_JAR:$WORK_DIR/mods" \ + --add-modules jpms.smoke \ + --output "$WORK_DIR/image" + +"$WORK_DIR/image/bin/java" -m jpms.smoke/org.apache.fory.jpms.Smoke | grep -qx "ok" + +IMAGE_MODULES="$("$WORK_DIR/image/bin/java" --list-modules)" +echo "$IMAGE_MODULES" | grep -q "^org.apache.fory.core" +echo "$IMAGE_MODULES" | grep -q "^org.apache.fory.format" +if echo "$IMAGE_MODULES" \ + | grep -Eq "^(java\.sql|com\.google|jsr305|org\.apache\.arrow|org\.slf4j|jdk\.incubator\.vector)"; then + echo "Optional static modules leaked into the minimal jlink image:" >&2 + echo "$IMAGE_MODULES" \ + | grep -E "^(java\.sql|com\.google|jsr305|org\.apache\.arrow|org\.slf4j|jdk\.incubator\.vector)" >&2 + exit 1 +fi + +if [[ "$JAVA_MAJOR" -ge 16 ]] \ + && jar tf "$CORE_JAR" | grep -qx "META-INF/versions/16/org/apache/fory/util/ArrayCompressionUtils.class"; then + mkdir -p "$WORK_DIR/vector-classes" + cat > "$WORK_DIR/VectorSmoke.java" <<'EOF' +import org.apache.fory.util.ArrayCompressionUtils; +import org.apache.fory.util.PrimitiveArrayCompressionType; + +public final class VectorSmoke { + public static void main(String[] args) throws Exception { + int[] values = new int[1024]; + for (int i = 0; i < values.length; i++) { + values[i] = (i & 0xff) - 128; + } + if (ArrayCompressionUtils.determineIntCompressionType(values) + != PrimitiveArrayCompressionType.INT_TO_BYTE) { + throw new AssertionError("Vector compression returned the wrong range"); + } + System.out.println("vector-ok"); + } +} +EOF + javac -cp "$CORE_JAR" -d "$WORK_DIR/vector-classes" "$WORK_DIR/VectorSmoke.java" + java \ + --add-modules jdk.incubator.vector \ + -cp "$CORE_JAR:$WORK_DIR/vector-classes" \ + VectorSmoke \ + | grep -qx "vector-ok" +fi + +echo "JPMS jlink smoke test passed with minimal optional dependencies." diff --git a/java/fory-core/pom.xml b/java/fory-core/pom.xml index 4176d74184..23d05bd6a8 100644 --- a/java/fory-core/pom.xml +++ b/java/fory-core/pom.xml @@ -78,6 +78,7 @@ org.apache.fory.core + true @@ -119,6 +120,7 @@ org.apache.fory.core + true @@ -135,6 +137,195 @@ + + jpms-java9 + + [9,) + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + compile-java9-module-info + process-classes + + run + + + + + + + + + + + + + + + + + + + inject-java9-module-info + package + + run + + + + + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + copy-java9-module-info + prepare-package + + copy-resources + + + ${project.build.outputDirectory}/META-INF/versions/9 + + + ${project.build.directory}/jpms-classes/java9 + + module-info.class + + + + + + + + + + + + jpms-java16 + + [16,) + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + compile-java16-sources + process-classes + + run + + + + + + + + + + + + + + + + + + + + + + + + clean-java16-package-classes + prepare-package + + run + + + + + + + + + inject-java16-classes + package + + run + + + + + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + copy-java16-classes + prepare-package + + copy-resources + + + ${project.build.outputDirectory}/META-INF/versions/16 + + + ${project.build.directory}/jpms-classes/java16 + + **/*.class + + + + + + + + + + xlang-parallel diff --git a/java/fory-core/src/main/java16/module-info.java b/java/fory-core/src/main/java16/module-info.java new file mode 100644 index 0000000000..40c773b6ac --- /dev/null +++ b/java/fory-core/src/main/java16/module-info.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +module org.apache.fory.core { + requires java.logging; + requires jdk.unsupported; + + requires static java.sql; + requires static com.google.common; + requires static org.slf4j; + requires static jsr305; + requires static jdk.incubator.vector; + + exports org.apache.fory; + exports org.apache.fory.annotation; + exports org.apache.fory.builder; + exports org.apache.fory.codegen; + exports org.apache.fory.collection; + exports org.apache.fory.config; + exports org.apache.fory.context; + exports org.apache.fory.exception; + exports org.apache.fory.io; + exports org.apache.fory.logging; + exports org.apache.fory.memory; + exports org.apache.fory.meta; + exports org.apache.fory.platform; + exports org.apache.fory.pool; + exports org.apache.fory.reflect; + exports org.apache.fory.resolver; + exports org.apache.fory.serializer; + exports org.apache.fory.serializer.collection; + exports org.apache.fory.serializer.converter; + exports org.apache.fory.serializer.scala; + exports org.apache.fory.serializer.struct; + exports org.apache.fory.type; + exports org.apache.fory.type.union; + exports org.apache.fory.type.unsigned; + exports org.apache.fory.util; + exports org.apache.fory.util.function; + exports org.apache.fory.util.record; + exports org.apache.fory.util.unsafe to + org.apache.fory.format; +} diff --git a/java/fory-simd/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java b/java/fory-core/src/main/java16/org/apache/fory/serializer/CompressedArraySerializers.java similarity index 99% rename from java/fory-simd/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java rename to java/fory-core/src/main/java16/org/apache/fory/serializer/CompressedArraySerializers.java index ba4f2e247a..9f4eee99cd 100644 --- a/java/fory-simd/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java +++ b/java/fory-core/src/main/java16/org/apache/fory/serializer/CompressedArraySerializers.java @@ -36,7 +36,7 @@ import org.apache.fory.util.PrimitiveArrayCompressionType; /** - * Compressed array serializers using Java 16+ Vector API for SIMD acceleration. + * Compressed array serializers with optional Java 16+ Vector API acceleration. * *

To use these serializers, simply call {@code CompressedArraySerializers.register(fory)} on * your Fory instance. These will override the default array serializers for {@code int[]} and diff --git a/java/fory-simd/src/main/java/org/apache/fory/util/ArrayCompressionUtils.java b/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java similarity index 60% rename from java/fory-simd/src/main/java/org/apache/fory/util/ArrayCompressionUtils.java rename to java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java index a55a53da8b..08d2111dfa 100644 --- a/java/fory-simd/src/main/java/org/apache/fory/util/ArrayCompressionUtils.java +++ b/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java @@ -25,75 +25,64 @@ import jdk.incubator.vector.VectorSpecies; /** - * Utility class for primitive array compression operations. It uses SIMD-accelerated array - * compression using Java 16+ Vector API. + * Utility methods for optional primitive array compression. * - *

This utility provides compression for primitive arrays by detecting when values can fit in - * smaller data types: + *

The compressed array serializers use these helpers when every value in a primitive array fits + * in a narrower primitive type: * *

    - *
  • int[] → byte[] when all values are in [-128, 127] range (75% size reduction) - *
  • int[] → short[] when all values are in [-32768, 32767] range (50% size reduction) - *
  • long[] → int[] when all values fit in integer range (50% size reduction) + *
  • {@code int[]} to {@code byte[]} when all values are in byte range. + *
  • {@code int[]} to {@code short[]} when all values are in short range. + *
  • {@code long[]} to {@code int[]} when all values are in int range. *
- * - *

Uses the best available compression provider via ServiceLoader pattern. SIMD optimizations are - * used when fory-simd module is available. When no compression provider is available, compression - * is disabled entirely. */ public final class ArrayCompressionUtils { + // Minimum array size to justify compression analysis and the compressed payload marker overhead. + static final int MIN_COMPRESSION_SIZE = 1 << 9; private static final VectorSpecies INT_SPECIES = IntVector.SPECIES_PREFERRED; private static final VectorSpecies LONG_SPECIES = LongVector.SPECIES_PREFERRED; - // Minimum array size to justify compression overhead - private static final int MIN_COMPRESSION_SIZE = 1 << 9; // 512 elements + private ArrayCompressionUtils() {} /** - * Determine the best compression type for int array. + * Determines the best compression type for an int array. * - * @param array the int array to analyze - * @return compression type (NONE, INT_TO_BYTE, or INT_TO_SHORT) - * @throws NullPointerException if array is null + * @param array the array to analyze + * @return {@link PrimitiveArrayCompressionType#INT_TO_BYTE}, {@link + * PrimitiveArrayCompressionType#INT_TO_SHORT}, or {@link PrimitiveArrayCompressionType#NONE} + * @throws NullPointerException if {@code array} is null */ public static PrimitiveArrayCompressionType determineIntCompressionType(int[] array) { if (array == null) { throw new NullPointerException("Input array cannot be null"); } if (array.length < MIN_COMPRESSION_SIZE) { - // No compression for empty or too small arrays return PrimitiveArrayCompressionType.NONE; } - boolean canCompressToByte = true; boolean canCompressToShort = true; - int i = 0; - final int upperBound = INT_SPECIES.loopBound(array.length); + int upperBound = INT_SPECIES.loopBound(array.length); - // SIMD loop + // Vector loop: test each lane against the target primitive ranges and stop checking a narrower + // representation once any lane exceeds its range. for (; i < upperBound && (canCompressToByte || canCompressToShort); i += INT_SPECIES.length()) { IntVector vector = IntVector.fromArray(INT_SPECIES, array, i); - - // Check byte compression using mask operations if (canCompressToByte) { - var byteMaxMask = vector.compare(VectorOperators.GT, Byte.MAX_VALUE); - var byteMinMask = vector.compare(VectorOperators.LT, Byte.MIN_VALUE); - if (byteMaxMask.anyTrue() || byteMinMask.anyTrue()) { + if (vector.compare(VectorOperators.GT, Byte.MAX_VALUE).anyTrue() + || vector.compare(VectorOperators.LT, Byte.MIN_VALUE).anyTrue()) { canCompressToByte = false; } } - - // Check short compression using mask operations if (canCompressToShort) { - var shortMaxMask = vector.compare(VectorOperators.GT, Short.MAX_VALUE); - var shortMinMask = vector.compare(VectorOperators.LT, Short.MIN_VALUE); - if (shortMaxMask.anyTrue() || shortMinMask.anyTrue()) { + if (vector.compare(VectorOperators.GT, Short.MAX_VALUE).anyTrue() + || vector.compare(VectorOperators.LT, Short.MIN_VALUE).anyTrue()) { canCompressToShort = false; } } } - // Handle remaining elements with scalar code + // Scalar tail for elements that do not fill a complete vector. for (; i < array.length && (canCompressToByte || canCompressToShort); i++) { int value = array[i]; if (canCompressToByte && (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE)) { @@ -103,22 +92,21 @@ public static PrimitiveArrayCompressionType determineIntCompressionType(int[] ar canCompressToShort = false; } } - if (canCompressToByte) { return PrimitiveArrayCompressionType.INT_TO_BYTE; - } else if (canCompressToShort) { - return PrimitiveArrayCompressionType.INT_TO_SHORT; - } else { - return PrimitiveArrayCompressionType.NONE; } + return canCompressToShort + ? PrimitiveArrayCompressionType.INT_TO_SHORT + : PrimitiveArrayCompressionType.NONE; } /** - * Determine the best compression type for long array. + * Determines the best compression type for a long array. * - * @param array the long array to analyze - * @return compression type (NONE or LONG_TO_INT) - * @throws NullPointerException if array is null + * @param array the array to analyze + * @return {@link PrimitiveArrayCompressionType#LONG_TO_INT} or {@link + * PrimitiveArrayCompressionType#NONE} + * @throws NullPointerException if {@code array} is null */ public static PrimitiveArrayCompressionType determineLongCompressionType(long[] array) { if (array == null) { @@ -127,42 +115,34 @@ public static PrimitiveArrayCompressionType determineLongCompressionType(long[] if (array.length < MIN_COMPRESSION_SIZE) { return PrimitiveArrayCompressionType.NONE; } - boolean canCompressToInt = true; - int i = 0; int upperBound = LONG_SPECIES.loopBound(array.length); - // SIMD loop - for (; i < upperBound && canCompressToInt; i += LONG_SPECIES.length()) { + // Vector loop: any lane outside int range means long-to-int compression is not safe. + for (; i < upperBound; i += LONG_SPECIES.length()) { LongVector vector = LongVector.fromArray(LONG_SPECIES, array, i); - - // Check int compression using mask operations - var maxMask = vector.compare(VectorOperators.GT, Integer.MAX_VALUE); - var minMask = vector.compare(VectorOperators.LT, Integer.MIN_VALUE); - if (maxMask.anyTrue() || minMask.anyTrue()) { - canCompressToInt = false; + if (vector.compare(VectorOperators.GT, Integer.MAX_VALUE).anyTrue() + || vector.compare(VectorOperators.LT, Integer.MIN_VALUE).anyTrue()) { + return PrimitiveArrayCompressionType.NONE; } } - // Handle remaining elements - for (; i < array.length && canCompressToInt; i++) { + // Scalar tail for elements that do not fill a complete vector. + for (; i < array.length; i++) { long value = array[i]; if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) { - canCompressToInt = false; + return PrimitiveArrayCompressionType.NONE; } } - - return canCompressToInt - ? PrimitiveArrayCompressionType.LONG_TO_INT - : PrimitiveArrayCompressionType.NONE; + return PrimitiveArrayCompressionType.LONG_TO_INT; } /** - * Compress int array to byte array. + * Compresses an int array to a byte array. * - * @param array the int array to compress + * @param array the int array to compress; values must be in byte range * @return compressed byte array - * @throws NullPointerException if array is null + * @throws NullPointerException if {@code array} is null */ public static byte[] compressToBytes(int[] array) { if (array == null) { @@ -176,11 +156,11 @@ public static byte[] compressToBytes(int[] array) { } /** - * Compress int array to short array. + * Compresses an int array to a short array. * - * @param array the int array to compress (values must be in short range) + * @param array the int array to compress; values must be in short range * @return compressed short array - * @throws NullPointerException if array is null + * @throws NullPointerException if {@code array} is null */ public static short[] compressToShorts(int[] array) { if (array == null) { @@ -194,11 +174,11 @@ public static short[] compressToShorts(int[] array) { } /** - * Compress long array to int array. + * Compresses a long array to an int array. * - * @param array the long array to compress (values must be in int range) + * @param array the long array to compress; values must be in int range * @return compressed int array - * @throws NullPointerException if array is null + * @throws NullPointerException if {@code array} is null */ public static int[] compressToInts(long[] array) { if (array == null) { @@ -212,11 +192,11 @@ public static int[] compressToInts(long[] array) { } /** - * Decompress byte array to int array. + * Decompresses a byte array to an int array. * * @param array the byte array to decompress * @return decompressed int array - * @throws NullPointerException if array is null + * @throws NullPointerException if {@code array} is null */ public static int[] decompressFromBytes(byte[] array) { if (array == null) { @@ -230,11 +210,11 @@ public static int[] decompressFromBytes(byte[] array) { } /** - * Decompress short array to int array. + * Decompresses a short array to an int array. * * @param array the short array to decompress * @return decompressed int array - * @throws NullPointerException if array is null + * @throws NullPointerException if {@code array} is null */ public static int[] decompressFromShorts(short[] array) { if (array == null) { @@ -248,17 +228,16 @@ public static int[] decompressFromShorts(short[] array) { } /** - * Decompress int array to long array. + * Decompresses an int array to a long array. * * @param array the int array to decompress * @return decompressed long array - * @throws NullPointerException if array is null + * @throws NullPointerException if {@code array} is null */ public static long[] decompressFromInts(int[] array) { if (array == null) { throw new NullPointerException("Array cannot be null"); } - long[] decompressed = new long[array.length]; for (int i = 0; i < array.length; i++) { decompressed[i] = array[i]; diff --git a/java/fory-simd/src/main/java/org/apache/fory/util/PrimitiveArrayCompressionType.java b/java/fory-core/src/main/java16/org/apache/fory/util/PrimitiveArrayCompressionType.java similarity index 82% rename from java/fory-simd/src/main/java/org/apache/fory/util/PrimitiveArrayCompressionType.java rename to java/fory-core/src/main/java16/org/apache/fory/util/PrimitiveArrayCompressionType.java index 61e7c6fe62..477b2037ba 100644 --- a/java/fory-simd/src/main/java/org/apache/fory/util/PrimitiveArrayCompressionType.java +++ b/java/fory-core/src/main/java16/org/apache/fory/util/PrimitiveArrayCompressionType.java @@ -23,18 +23,19 @@ * Compression types for primitive arrays. * *

Defines the available compression strategies for reducing the size of primitive arrays by - * detecting when values can fit in smaller data types. + * detecting when values can be stored with a narrower primitive type. */ public enum PrimitiveArrayCompressionType { - // No compression applied + /** No compression applied. */ NONE(0), - // Compression for int arrays: - // int[] → byte[] compression (75% size reduction) + /** Compresses {@code int[]} values to {@code byte[]} when every value fits in byte range. */ INT_TO_BYTE(1), - // int[] → short[] compression (50% size reduction) + + /** Compresses {@code int[]} values to {@code short[]} when every value fits in short range. */ INT_TO_SHORT(2), - // Compression for long arrays: long[] → int[] compression (50% size reduction) + + /** Compresses {@code long[]} values to {@code int[]} when every value fits in int range. */ LONG_TO_INT(3); private final int value; @@ -46,7 +47,7 @@ public enum PrimitiveArrayCompressionType { /** * Gets the numeric value for this compression type. * - * @return the numeric value + * @return the numeric value written to the serialized payload */ public int getValue() { return value; @@ -55,7 +56,7 @@ public int getValue() { /** * Gets the compression type from its numeric value. * - * @param value the numeric value + * @param value the numeric value read from the serialized payload * @return the corresponding compression type * @throws IllegalArgumentException if the value is not valid */ @@ -76,6 +77,8 @@ public static PrimitiveArrayCompressionType fromValue(int value) { /** Compression utilities for int arrays. Supports compression to byte[] and short[] formats. */ public static final class IntArrayCompression { + private IntArrayCompression() {} + /** * Determines the best compression type for the given int array. * @@ -97,7 +100,10 @@ public static boolean isSupported(PrimitiveArrayCompressionType type) { } } + /** Compression utilities for long arrays. Supports compression to int[] format. */ public static final class LongArrayCompression { + private LongArrayCompression() {} + /** * Determines the best compression type for the given long array. * diff --git a/java/fory-core/src/main/java9/module-info.java b/java/fory-core/src/main/java9/module-info.java new file mode 100644 index 0000000000..e381802cae --- /dev/null +++ b/java/fory-core/src/main/java9/module-info.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +module org.apache.fory.core { + requires java.logging; + requires jdk.unsupported; + + requires static java.sql; + requires static com.google.common; + requires static org.slf4j; + requires static jsr305; + + exports org.apache.fory; + exports org.apache.fory.annotation; + exports org.apache.fory.builder; + exports org.apache.fory.codegen; + exports org.apache.fory.collection; + exports org.apache.fory.config; + exports org.apache.fory.context; + exports org.apache.fory.exception; + exports org.apache.fory.io; + exports org.apache.fory.logging; + exports org.apache.fory.memory; + exports org.apache.fory.meta; + exports org.apache.fory.platform; + exports org.apache.fory.pool; + exports org.apache.fory.reflect; + exports org.apache.fory.resolver; + exports org.apache.fory.serializer; + exports org.apache.fory.serializer.collection; + exports org.apache.fory.serializer.converter; + exports org.apache.fory.serializer.scala; + exports org.apache.fory.serializer.struct; + exports org.apache.fory.type; + exports org.apache.fory.type.union; + exports org.apache.fory.type.unsigned; + exports org.apache.fory.util; + exports org.apache.fory.util.function; + exports org.apache.fory.util.record; + exports org.apache.fory.util.unsafe to + org.apache.fory.format; +} diff --git a/java/fory-format/pom.xml b/java/fory-format/pom.xml index 01cdd773ec..a1fecb2ef1 100644 --- a/java/fory-format/pom.xml +++ b/java/fory-format/pom.xml @@ -106,6 +106,7 @@ org.apache.fory.format + true @@ -121,6 +122,71 @@ + + jpms-java11 + + [11,) + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + compile-java11-module-info + process-classes + + run + + + + + + + + + + + + + + + + + + + + + + + inject-java11-module-info + package + + run + + + + + + + + + + + + + + releaseShade @@ -170,6 +236,8 @@ *:* + module-info.class + META-INF/versions/**/module-info.class META-INF/*.SF META-INF/*.DSA META-INF/*.RSA diff --git a/java/fory-format/src/main/java/org/apache/fory/format/encoder/LazyArrayData.java b/java/fory-format/src/main/java/org/apache/fory/format/encoder/LazyArrayData.java index 38a9b92b57..c75f5b222d 100644 --- a/java/fory-format/src/main/java/org/apache/fory/format/encoder/LazyArrayData.java +++ b/java/fory-format/src/main/java/org/apache/fory/format/encoder/LazyArrayData.java @@ -21,7 +21,7 @@ import static org.apache.fory.type.TypeUtils.getRawType; -import java.awt.List; +import java.util.List; import org.apache.fory.annotation.Internal; import org.apache.fory.codegen.ClosureVisitable; import org.apache.fory.codegen.Code; diff --git a/java/fory-format/src/main/java11/module-info.java b/java/fory-format/src/main/java11/module-info.java new file mode 100644 index 0000000000..0f6064b8e9 --- /dev/null +++ b/java/fory-format/src/main/java11/module-info.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +module org.apache.fory.format { + requires transitive org.apache.fory.core; + + requires static java.sql; + requires static transitive org.apache.arrow.memory.core; + requires static transitive org.apache.arrow.vector; + + exports org.apache.fory.format.encoder; + exports org.apache.fory.format.row; + exports org.apache.fory.format.row.binary; + exports org.apache.fory.format.row.binary.writer; + exports org.apache.fory.format.type; + exports org.apache.fory.format.vectorized; +} diff --git a/java/fory-simd/pom.xml b/java/fory-simd/pom.xml deleted file mode 100644 index 675e183748..0000000000 --- a/java/fory-simd/pom.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - org.apache.fory - fory-parent - 1.1.0-SNAPSHOT - - 4.0.0 - - fory-simd - - - Apache Fory™ is a blazingly fast multi-language serialization framework powered by jit and zero-copy. - - - - 17 - 17 - ${basedir}/.. - - - - - org.apache.fory - fory-core - ${project.version} - - - - - org.testng - testng - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 16 - 16 - - --add-modules=jdk.incubator.vector - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - --add-modules=jdk.incubator.vector - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - false - - --add-modules=jdk.incubator.vector - - - - - - diff --git a/java/fory-testsuite/pom.xml b/java/fory-testsuite/pom.xml index 7831d56dc9..7363073431 100644 --- a/java/fory-testsuite/pom.xml +++ b/java/fory-testsuite/pom.xml @@ -142,4 +142,63 @@ + + + java16-compression-tests + + [16,) + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + compile-java16-compression-tests + test-compile + + run + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + --add-modules=jdk.incubator.vector + + ${project.basedir}/../fory-core/target/jpms-classes/java16 + + + + + + + + diff --git a/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java b/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionTest.java similarity index 99% rename from java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java rename to java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionTest.java index 6650264107..a248b84c34 100644 --- a/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java +++ b/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionTest.java @@ -29,7 +29,6 @@ import org.testng.annotations.Test; public class ArrayCompressionTest { - @DataProvider(name = "intArrayData") public Object[][] intArrayData() { return new Object[][] { diff --git a/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionUtilsTest.java b/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionUtilsTest.java similarity index 99% rename from java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionUtilsTest.java rename to java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionUtilsTest.java index 29145b7a7a..6c3b64a0ad 100644 --- a/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionUtilsTest.java +++ b/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionUtilsTest.java @@ -28,7 +28,6 @@ import org.testng.annotations.Test; public class ArrayCompressionUtilsTest { - @Test public void testIntArrayCompressionDetection() { // Test byte range compression - make array size >= 512 diff --git a/java/pom.xml b/java/pom.xml index 4e1d14ae43..473b2c6a9e 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -105,7 +105,6 @@ [21,] - fory-simd fory-latest-jdk-tests