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.classMETA-INF/*.SFMETA-INF/*.DSAMETA-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-simdfory-latest-jdk-tests