This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new edf2155563 feat: migrate native-io implementation from C to Rust 
(#4738)
edf2155563 is described below

commit edf21555638800c31bb3b8d6c72d18c39a4ff056
Author: Zixuan Liu <[email protected]>
AuthorDate: Wed Apr 15 23:41:49 2026 +0800

    feat: migrate native-io implementation from C to Rust (#4738)
    
    * feat: migrate native-io implementation from C to Rust
    
    * fix: use Apache Commons SystemUtils for OS name and architecture retrieval
    
    * feat: add support for pure Rust native-io builds with conditional 
activation
    
    * chore: update Rust and Zig toolchain versions
    
    * chore: extract native build setup action
    
    Co-authored-by: Copilot <[email protected]>
    
    * fix: add shell to native build action
    
    Co-authored-by: Copilot <[email protected]>
    
    ---------
    
    Co-authored-by: Copilot <[email protected]>
---
 .github/actions/setup-native-build/action.yml      |  38 +++
 .github/workflows/bk-ci.yml                        |  22 ++
 .github/workflows/bk-streamstorage-python.yml      |   4 +-
 .github/workflows/codeql.yml                       |   4 +
 .github/workflows/java21-daily-build.yml           |   2 +
 .github/workflows/windows-daily-build.yml          |   2 +
 native-io/pom.xml                                  | 281 ++++++++---------
 native-io/src/main/assembly/assembly.xml           |  62 ----
 .../common/util/nativeio/NativeIOJni.java          |  35 ++-
 .../common/util/nativeio/NativeIOLibraryPath.java  | 124 ++++++++
 .../src/main/native-io-jni/cpp/native_io_jni.c     | 325 -------------------
 native-io/src/main/native-io-jni/rust/Cargo.lock   | 301 ++++++++++++++++++
 native-io/src/main/native-io-jni/rust/Cargo.toml   |  41 +++
 native-io/src/main/native-io-jni/rust/build.rs     |  37 +++
 native-io/src/main/native-io-jni/rust/src/lib.rs   | 346 +++++++++++++++++++++
 .../util/nativeio/NativeIOLibraryPathTest.java     | 100 ++++++
 pom.xml                                            |   3 +
 17 files changed, 1185 insertions(+), 542 deletions(-)

diff --git a/.github/actions/setup-native-build/action.yml 
b/.github/actions/setup-native-build/action.yml
new file mode 100644
index 0000000000..4c9c234c5c
--- /dev/null
+++ b/.github/actions/setup-native-build/action.yml
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+name: setup native build
+description: sets up Rust, Zig, and cargo-zigbuild for native builds
+runs:
+  using: composite
+  steps:
+    - name: Set up Rust
+      uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8
+      with:
+        # Cross-compilation targets: Linux amd64 and arm64 (glibc)
+        targets: >-
+          x86_64-unknown-linux-gnu,
+          aarch64-unknown-linux-gnu
+
+    - name: Set up Zig
+      uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29
+
+    - name: Install cargo-zigbuild
+      run: cargo install --locked cargo-zigbuild
+      shell: bash
diff --git a/.github/workflows/bk-ci.yml b/.github/workflows/bk-ci.yml
index 64116a3fe6..582d9589b6 100644
--- a/.github/workflows/bk-ci.yml
+++ b/.github/workflows/bk-ci.yml
@@ -82,6 +82,10 @@ jobs:
           distribution: 'temurin'
           java-version: 17
 
+      - name: Setup native build
+        if: steps.check_changes.outputs.docs_only != 'true'
+        uses: ./.github/actions/setup-native-build
+
       - name: Validate pull request
         if: steps.check_changes.outputs.docs_only != 'true'
         run: |
@@ -161,6 +165,9 @@ jobs:
           distribution: 'temurin'
           java-version: 17
 
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
+
       - name: Tune Java DNS TTL settings
         run: |
           sudo tee -a $JAVA_HOME/conf/security/java.security <<EOF
@@ -244,6 +251,9 @@ jobs:
           distribution: 'temurin'
           java-version: 17
 
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
+
       - name: Tune Java DNS TTL settings
         run: |
           sudo tee -a $JAVA_HOME/conf/security/java.security <<EOF
@@ -341,6 +351,9 @@ jobs:
           distribution: 'temurin'
           java-version: 17
 
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
+
       - name: Restore released versions cache
         uses: actions/cache/restore@v4
         with:
@@ -437,6 +450,9 @@ jobs:
           distribution: 'temurin'
           java-version: 17
 
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
+
       - name: mvn package
         run: mvn -B -nsu clean package -DskipTests
 
@@ -471,6 +487,9 @@ jobs:
           distribution: 'temurin'
           java-version: 17
 
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
+
       - name: mvn package
         run: mvn -B -nsu clean package -DskipTests
 
@@ -514,6 +533,9 @@ jobs:
           distribution: 'temurin'
           java-version: ${{ matrix.jdk_version }}
 
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
+
       - name: Build with Maven
         run: mvn clean package -B -nsu -DskipBookKeeperServerTests
 
diff --git a/.github/workflows/bk-streamstorage-python.yml 
b/.github/workflows/bk-streamstorage-python.yml
index 097b18a9b4..5e696e052a 100644
--- a/.github/workflows/bk-streamstorage-python.yml
+++ b/.github/workflows/bk-streamstorage-python.yml
@@ -73,6 +73,8 @@ jobs:
         with:
           distribution: 'temurin'
           java-version: 17
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
       - name: Build
         run: mvn -q -T 1C -B -nsu clean install -DskipTests -Dcheckstyle.skip 
-Dspotbugs.skip -Drat.skip -Dmaven.javadoc.skip
       - name: Pick ubuntu mirror for the docker image build
@@ -83,5 +85,3 @@ jobs:
         run: ./stream/clients/python/docker/build-local-image.sh
       - name: Test
         run: ./stream/clients/python/scripts/docker_integration_tests.sh
-
-
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 156468c5f3..ea7da56263 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -85,6 +85,10 @@ jobs:
         distribution: 'temurin'
         java-version: 17
 
+    - name: Setup native build
+      if: steps.check_changes.outputs.docs_only != 'true'
+      uses: ./.github/actions/setup-native-build
+
     - name: Validate pull request
       if: steps.check_changes.outputs.docs_only != 'true'
       run: |
diff --git a/.github/workflows/java21-daily-build.yml 
b/.github/workflows/java21-daily-build.yml
index 331ad9b61c..961d60b14d 100644
--- a/.github/workflows/java21-daily-build.yml
+++ b/.github/workflows/java21-daily-build.yml
@@ -42,6 +42,8 @@ jobs:
         with:
           distribution: 'temurin'
           java-version: 21
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
       - name: Build with Maven
         run: mvn -B clean install
       - name: Aggregates all test reports to ./test-reports and 
./surefire-reports directories If failure
diff --git a/.github/workflows/windows-daily-build.yml 
b/.github/workflows/windows-daily-build.yml
index 513c28241b..25035b49c7 100644
--- a/.github/workflows/windows-daily-build.yml
+++ b/.github/workflows/windows-daily-build.yml
@@ -43,6 +43,8 @@ jobs:
         with:
           distribution: 'temurin'
           java-version: 21
+      - name: Setup native build
+        uses: ./.github/actions/setup-native-build
       - name: Build with Maven
         run: mvn -B clean install
       - name: Aggregates all test reports to ./test-reports and 
./surefire-reports directories If failure
diff --git a/native-io/pom.xml b/native-io/pom.xml
index 923ea43dbe..fe3d15820b 100644
--- a/native-io/pom.xml
+++ b/native-io/pom.xml
@@ -24,13 +24,19 @@
   </parent>
 
   <artifactId>native-io</artifactId>
-  <packaging>nar</packaging>
+  <packaging>jar</packaging>
   <name>Apache BookKeeper :: Native IO Library</name>
   <description>Native IO Library</description>
 
   <properties>
-    <nar.runtime>dynamic</nar.runtime>
-    <nar.cpp.optionSet>-msse4.2 -mpclmul</nar.cpp.optionSet>
+    <rust.dir>${project.basedir}/src/main/native-io-jni/rust</rust.dir>
+    <rust.target.dir>${rust.dir}/target</rust.target.dir>
+    <rust.executable>cargo</rust.executable>
+    
<rust.release.lib.relative.path>release/libnative_io.so</rust.release.lib.relative.path>
+    
<rust.zig.target.amd64.gnu>x86_64-unknown-linux-gnu</rust.zig.target.amd64.gnu>
+    
<rust.zig.target.arm64.gnu>aarch64-unknown-linux-gnu</rust.zig.target.arm64.gnu>
+    <rust.lib.dir>${project.build.directory}/classes/lib</rust.lib.dir>
+    <native.io.pure.rust>false</native.io.pure.rust>
   </properties>
 
   <dependencies>
@@ -43,6 +49,12 @@
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
     </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -51,175 +63,156 @@
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
       </plugin>
-      <plugin>
-        <groupId>com.github.maven-nar</groupId>
-        <artifactId>nar-maven-plugin</artifactId>
-        <extensions>true</extensions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <configuration>
-          <descriptors>
-            <descriptor>src/main/assembly/assembly.xml</descriptor>
-          </descriptors>
-          <appendAssemblyId>false</appendAssemblyId>
-          <tarLongFileMode>posix</tarLongFileMode>
-        </configuration>
-        <executions>
-          <execution>
-            <id>make-assembly</id>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
       <plugin>
         <groupId>org.apache.rat</groupId>
         <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/rust/target/**</exclude>
+          </excludes>
+        </configuration>
       </plugin>
     </plugins>
   </build>
 
   <profiles>
     <profile>
-      <!-- from JDK10 javah command is not available
-           see http://openjdk.java.net/jeps/313
+      <!--
+        Cross-compile for Linux amd64 + arm64 (glibc) via cargo-zigbuild.
+        Produces a single JAR containing two variants:
+          lib/linux-x86_64-gnu/libnative-io.so   (glibc, amd64)
+          lib/linux-aarch64-gnu/libnative-io.so  (glibc, arm64)
+
+        Prerequisites: zig, cargo-zigbuild, and both Rust targets
+        added via `rustup target add`.
       -->
-      <id>jdk-without-javah</id>
+      <id>cargo-zigbuild</id>
       <activation>
-         <jdk>[10,)</jdk>
+        <property>
+          <name>native.io.pure.rust</name>
+          <value>!true</value>
+        </property>
       </activation>
       <build>
         <plugins>
           <plugin>
-            <groupId>com.github.maven-nar</groupId>
-            <artifactId>nar-maven-plugin</artifactId>
-            <extensions>true</extensions>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>exec-maven-plugin</artifactId>
             <executions>
-               <execution>
-               <!-- javah is not present in JDK10 onwards,
-                    you have to to use javac -h -->
-                  <id>default-nar-javah</id>
-                  <phase>none</phase>
-               </execution>
-             </executions>
-          </plugin>
-          <plugin>
-             <groupId>org.apache.maven.plugins</groupId>
-             <artifactId>maven-compiler-plugin</artifactId>
-             <configuration>
-                <compilerArgs>
-                  <!-- add -h flag to javac -->
-                  <compilerArg>-h</compilerArg>
-                  
<compilerArg>${project.build.directory}/nar/javah-include</compilerArg>
-                </compilerArgs>
-             </configuration>
-          </plugin>
-            </plugins>
-        </build>
-    </profile>
-    <profile>
-      <id>mac</id>
-      <activation>
-        <os>
-          <name>Mac OS X</name>
-        </os>
-      </activation>
-      <build>
-        <plugins>
-          <plugin>
-            <groupId>com.github.maven-nar</groupId>
-            <artifactId>nar-maven-plugin</artifactId>
-            <extensions>true</extensions>
-            <configuration>
-              <runtime>${nar.runtime}</runtime>
-              <output>native-io</output>
-              <libraries>
-                <library>
-                  <type>jni</type>
-                  
<narSystemPackage>org.apache.bookkeeper.util.nativeio</narSystemPackage>
-                </library>
-              </libraries>
-              <cpp>
-                <optionSet>${nar.cpp.optionSet}</optionSet>
-                <exceptions>false</exceptions>
-                <rtti>false</rtti>
-                <optimize>full</optimize>
-              </cpp>
-            </configuration>
+              <execution>
+                <id>cargo-zigbuild-amd64-gnu</id>
+                <phase>compile</phase>
+                <goals><goal>exec</goal></goals>
+                <configuration>
+                  <executable>${rust.executable}</executable>
+                  <workingDirectory>${rust.dir}</workingDirectory>
+                  <arguments>
+                    <argument>zigbuild</argument>
+                    <argument>--release</argument>
+                    <argument>--target</argument>
+                    <argument>${rust.zig.target.amd64.gnu}</argument>
+                  </arguments>
+                </configuration>
+              </execution>
+              <execution>
+                <id>cargo-zigbuild-arm64-gnu</id>
+                <phase>compile</phase>
+                <goals><goal>exec</goal></goals>
+                <configuration>
+                  <executable>${rust.executable}</executable>
+                  <workingDirectory>${rust.dir}</workingDirectory>
+                  <arguments>
+                    <argument>zigbuild</argument>
+                    <argument>--release</argument>
+                    <argument>--target</argument>
+                    <argument>${rust.zig.target.arm64.gnu}</argument>
+                  </arguments>
+                </configuration>
+              </execution>
+            </executions>
           </plugin>
-        </plugins>
-      </build>
-    </profile>
-
-    <profile>
-      <id>Linux</id>
-      <activation>
-        <os>
-          <name>Linux</name>
-        </os>
-      </activation>
-      <build>
-        <plugins>
           <plugin>
-            <groupId>com.github.maven-nar</groupId>
-            <artifactId>nar-maven-plugin</artifactId>
-            <extensions>true</extensions>
-            <configuration>
-              <runtime>${nar.runtime}</runtime>
-              <output>native-io</output>
-              <libraries>
-                <library>
-                  <type>jni</type>
-                  
<narSystemPackage>org.apache.bookkeeper.util.nativeio</narSystemPackage>
-                </library>
-              </libraries>
-              <cpp>
-                <optionSet>${nar.cpp.optionSet}</optionSet>
-                <exceptions>false</exceptions>
-                <rtti>false</rtti>
-                <optimize>full</optimize>
-              </cpp>
-            </configuration>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>copy-zig-libs</id>
+                <phase>process-classes</phase>
+                <goals><goal>run</goal></goals>
+                <configuration>
+                  <target>
+                    <mkdir dir="${rust.lib.dir}/linux-x86_64-gnu"/>
+                    <mkdir dir="${rust.lib.dir}/linux-aarch64-gnu"/>
+                    <copy 
file="${rust.target.dir}/${rust.zig.target.amd64.gnu}/${rust.release.lib.relative.path}"
+                          
tofile="${rust.lib.dir}/linux-x86_64-gnu/libnative-io.so"
+                          failonerror="true" verbose="true"/>
+                    <copy 
file="${rust.target.dir}/${rust.zig.target.arm64.gnu}/${rust.release.lib.relative.path}"
+                          
tofile="${rust.lib.dir}/linux-aarch64-gnu/libnative-io.so"
+                          failonerror="true" verbose="true"/>
+                  </target>
+                </configuration>
+              </execution>
+            </executions>
           </plugin>
         </plugins>
       </build>
     </profile>
     <profile>
-      <id>Windows</id>
+      <!--
+        Opt-in local build that uses plain cargo for the current Linux host.
+        Enable with: mvn -pl native-io -Dnative.io.pure.rust=true test
+      -->
+      <id>pure-rust-build</id>
       <activation>
-        <os>
-          <family>Windows</family>
-        </os>
+        <property>
+          <name>native.io.pure.rust</name>
+          <value>true</value>
+        </property>
       </activation>
       <build>
         <plugins>
           <plugin>
-            <groupId>com.github.maven-nar</groupId>
-            <artifactId>nar-maven-plugin</artifactId>
-            <extensions>true</extensions>
-            <configuration>
-              <runtime>${nar.runtime}</runtime>
-              <output>native-io</output>
-              <libraries>
-                <library>
-                  <type>jni</type>
-                  
<narSystemPackage>org.apache.bookkeeper.util.nativeio</narSystemPackage>
-                </library>
-              </libraries>
-              <cpp>
-                <optionSet>${nar.cpp.optionSet}</optionSet>
-                <exceptions>false</exceptions>
-                <rtti>false</rtti>
-                <optimize>full</optimize>
-              </cpp>
-              <linker>
-                <name>g++</name>
-              </linker>
-            </configuration>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>build-and-copy-pure-rust-lib</id>
+                <phase>process-classes</phase>
+                <goals><goal>run</goal></goals>
+                <configuration>
+                  <target>
+                    <fail message="Pure Rust native-io builds are supported on 
Linux hosts only.">
+                      <condition>
+                        <not>
+                          <equals arg1="${os.detected.name}" arg2="linux"/>
+                        </not>
+                      </condition>
+                    </fail>
+                    <condition property="rust.current.jar.directory" 
value="linux-x86_64-gnu">
+                      <or>
+                        <equals arg1="${os.detected.arch}" arg2="x86_64"/>
+                        <equals arg1="${os.detected.arch}" arg2="amd64"/>
+                      </or>
+                    </condition>
+                    <condition property="rust.current.jar.directory" 
value="linux-aarch64-gnu">
+                      <or>
+                        <equals arg1="${os.detected.arch}" arg2="aarch64"/>
+                        <equals arg1="${os.detected.arch}" arg2="arm64"/>
+                        <equals arg1="${os.detected.arch}" arg2="aarch_64"/>
+                      </or>
+                    </condition>
+                    <fail unless="rust.current.jar.directory"
+                          message="Pure Rust native-io builds are supported on 
Linux amd64/aarch64 hosts only."/>
+                    <mkdir 
dir="${rust.lib.dir}/${rust.current.jar.directory}"/>
+                    <exec executable="${rust.executable}" dir="${rust.dir}" 
failonerror="true">
+                      <arg value="build"/>
+                      <arg value="--release"/>
+                    </exec>
+                    <copy 
file="${rust.target.dir}/${rust.release.lib.relative.path}"
+                          
tofile="${rust.lib.dir}/${rust.current.jar.directory}/libnative-io.so"
+                          failonerror="true" verbose="true"/>
+                  </target>
+                </configuration>
+              </execution>
+            </executions>
           </plugin>
         </plugins>
       </build>
diff --git a/native-io/src/main/assembly/assembly.xml 
b/native-io/src/main/assembly/assembly.xml
deleted file mode 100644
index 377b97ff54..0000000000
--- a/native-io/src/main/assembly/assembly.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    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.
-
--->
-<assembly>
-       <id>all</id>
-       <formats>
-               <format>jar</format>
-       </formats>
-
-       <includeBaseDirectory>false</includeBaseDirectory>
-       <fileSets>
-               <fileSet>
-                       
<directory>${project.build.directory}/nar/${project.artifactId}-${project.version}-${os.arch}-MacOSX-gpp-jni/lib/${os.arch}-MacOSX-gpp/jni
-                       </directory>
-                       <outputDirectory>lib</outputDirectory>
-                       <includes>
-                               <include>lib*</include>
-                       </includes>
-               </fileSet>
-               <fileSet>
-                       
<directory>${project.build.directory}/nar/${project.artifactId}-${project.version}-${os.arch}-Linux-gpp-jni/lib/${os.arch}-Linux-gpp/jni
-                       </directory>
-                       <outputDirectory>lib</outputDirectory>
-                       <includes>
-                               <include>lib*</include>
-                       </includes>
-               </fileSet>
-               <fileSet>
-                       
<directory>${project.build.directory}/nar/${project.artifactId}-${project.version}-${os.arch}-${os.name}-gpp-jni/lib/${os.arch}-${os.name}-gpp/jni
-                       </directory>
-                       <outputDirectory>lib</outputDirectory>
-                       <includes>
-                               <include>lib*</include>
-                       </includes>
-               </fileSet>
-               <fileSet>
-                       
<directory>${project.build.directory}/classes</directory>
-                       <outputDirectory></outputDirectory>
-                       <includes>
-                               <include>**/*</include>
-                       </includes>
-               </fileSet>
-       </fileSets>
-</assembly>
diff --git 
a/native-io/src/main/java/org/apache/bookkeeper/common/util/nativeio/NativeIOJni.java
 
b/native-io/src/main/java/org/apache/bookkeeper/common/util/nativeio/NativeIOJni.java
index beaeff24ac..b4b9e63aed 100644
--- 
a/native-io/src/main/java/org/apache/bookkeeper/common/util/nativeio/NativeIOJni.java
+++ 
b/native-io/src/main/java/org/apache/bookkeeper/common/util/nativeio/NativeIOJni.java
@@ -20,6 +20,7 @@
  */
 package org.apache.bookkeeper.common.util.nativeio;
 
+import java.util.List;
 import org.apache.bookkeeper.common.util.nativelib.NativeUtils;
 import org.apache.commons.lang3.SystemUtils;
 
@@ -50,16 +51,32 @@ class NativeIOJni {
     static native int close(int fd) throws NativeIOException;
 
     static {
-        try {
-            if (SystemUtils.IS_OS_MAC_OSX) {
-                NativeUtils.loadLibraryFromJar("/lib/libnative-io.jnilib");
-            } else if (SystemUtils.IS_OS_LINUX) {
-                NativeUtils.loadLibraryFromJar("/lib/libnative-io.so");
-            } else {
-                throw new RuntimeException("OS not supported by Native-IO 
utils");
+        String explicitPath = NativeIOLibraryPath.configuredLibraryPath();
+        if (explicitPath != null) {
+            System.load(explicitPath);
+        } else {
+            List<String> candidates = 
NativeIOLibraryPath.currentPlatformLibraryCandidates();
+            if (candidates.isEmpty()) {
+                throw new IllegalStateException("No native-io JNI library 
candidates found for platform "
+                        + SystemUtils.OS_NAME + "/" + SystemUtils.OS_ARCH);
+            }
+
+            boolean loaded = false;
+            Throwable lastFailure = null;
+            for (String candidate : candidates) {
+                try {
+                    NativeUtils.loadLibraryFromJar(candidate);
+                    loaded = true;
+                    break;
+                } catch (Exception | UnsatisfiedLinkError e) {
+                    lastFailure = e;
+                }
+            }
+
+            if (!loaded) {
+                throw new IllegalStateException("Failed to load any native-io 
JNI library candidate for platform "
+                        + SystemUtils.OS_NAME + "/" + SystemUtils.OS_ARCH, 
lastFailure);
             }
-        } catch (Exception e) {
-            throw new RuntimeException(e);
         }
     }
 }
diff --git 
a/native-io/src/main/java/org/apache/bookkeeper/common/util/nativeio/NativeIOLibraryPath.java
 
b/native-io/src/main/java/org/apache/bookkeeper/common/util/nativeio/NativeIOLibraryPath.java
new file mode 100644
index 0000000000..fdc1ea2b4e
--- /dev/null
+++ 
b/native-io/src/main/java/org/apache/bookkeeper/common/util/nativeio/NativeIOLibraryPath.java
@@ -0,0 +1,124 @@
+/*
+ * 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 org.apache.bookkeeper.common.util.nativeio;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Resolves the path of the native-io shared library inside the JAR.
+ *
+ * <p>The {@code cargo-zigbuild} Maven profile embeds two variants:
+ * <pre>
+ *   lib/linux-x86_64-gnu/libnative-io.so   (glibc, amd64)
+ *   lib/linux-aarch64-gnu/libnative-io.so  (glibc, arm64)
+ * </pre>
+ *
+ * <p>An explicit override path can be set via the system property
+ * {@code bookkeeper.native.io.library.path} or the environment variable
+ * {@code BOOKKEEPER_NATIVE_IO_LIBRARY_PATH}.
+ */
+public class NativeIOLibraryPath {
+
+    private static final String LIBRARY_PATH_ENV = 
"BOOKKEEPER_NATIVE_IO_LIBRARY_PATH";
+    private static final String LIBRARY_PATH_PROPERTY = 
"bookkeeper.native.io.library.path";
+
+    private NativeIOLibraryPath() {
+    }
+
+    /**
+     * Returns an explicit path from system property / env, or {@code null}.
+     */
+    public static String configuredLibraryPath() {
+        return configuredLibraryPath(
+                System.getProperty(LIBRARY_PATH_PROPERTY),
+                System.getenv(LIBRARY_PATH_ENV));
+    }
+
+    protected static String configuredLibraryPath(String propertyValue, String 
envValue) {
+        return StringUtils.isNotBlank(propertyValue) ? propertyValue : 
StringUtils.stripToNull(envValue);
+    }
+
+    /**
+     * Returns the ordered list of JAR-resource paths to try for the current
+     * platform and architecture. The first path that successfully loads wins.
+     */
+    public static List<String> currentPlatformLibraryCandidates() {
+        if (SystemUtils.IS_OS_LINUX) {
+            return libraryCandidates(SystemUtils.OS_NAME, SystemUtils.OS_ARCH);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    protected static List<String> libraryCandidates(String osName, String 
osArch) {
+        List<String> paths = new ArrayList<>();
+        String osNameTag = osNameTag(osName);
+        String archTag = archTag(osArch);
+        paths.add("/lib/" + osNameTag + "-" + archTag + 
"-gnu/libnative-io.so");
+        return paths;
+    }
+
+    private static String osNameTag(String osName) {
+        if (osName == null) {
+            throw new IllegalArgumentException("osName is null");
+        }
+
+        String normalized = osName.toLowerCase(Locale.US);
+
+        if (normalized.contains("mac") || normalized.contains("darwin")) {
+            return "macos";
+        }
+
+        if (normalized.startsWith("win")
+                || normalized.contains("mingw")
+                || normalized.contains("msys")
+                || normalized.contains("cygwin")) {
+            return "windows";
+        }
+
+        if (normalized.contains("linux")) {
+            return "linux";
+        }
+
+        throw new IllegalArgumentException("Unsupported OS: " + osName);
+    }
+
+    private static final Pattern NON_ALNUM = Pattern.compile("[^a-z0-9]");
+
+    private static String archTag(String osArch) {
+        if (osArch == null) {
+            throw new IllegalArgumentException("osArch is null");
+        }
+
+        String arch = 
NON_ALNUM.matcher(osArch.toLowerCase(Locale.US)).replaceAll("");
+
+        return switch (arch) {
+            case "amd64", "x8664", "x64" -> "x86_64";
+            case "aarch64", "arm64", "armv8", "armv8l" -> "aarch64";
+            default -> throw new IllegalArgumentException("Unsupported arch: " 
+ osArch);
+        };
+    }
+}
diff --git a/native-io/src/main/native-io-jni/cpp/native_io_jni.c 
b/native-io/src/main/native-io-jni/cpp/native_io_jni.c
deleted file mode 100644
index 7be468518b..0000000000
--- a/native-io/src/main/native-io-jni/cpp/native_io_jni.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- *
- * 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.
- *
- */
-#define _GNU_SOURCE
-
-#include <jni.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef _WIN32
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-
-#include <org_apache_bookkeeper_common_util_nativeio_NativeIOJni.h>
-
-#ifdef _WIN32
-
-#define fsync(fd) fflush(fd)
-#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
-
-static ssize_t pread (int fd, void *buf, size_t count, off_t offset)
-{
-  ssize_t res;
-  off_t ooffset;
-
-  ooffset = lseek (fd, 0, SEEK_CUR);
-  lseek (fd, offset, SEEK_SET);
-  res = read (fd, buf, count);
-  lseek (fd, ooffset, SEEK_SET);
-
-  return res;
-}
-
-static ssize_t pwrite (int fd, void *buf, size_t count, off_t offset)
-{
-  ssize_t res;
-  off_t ooffset;
-
-  ooffset = lseek (fd, 0, SEEK_CUR);
-  lseek (fd, offset, SEEK_SET);
-  res = write (fd, buf, count);
-  lseek (fd, ooffset, SEEK_SET);
-
-  return res;
-}
-
-static int check_align(size_t align)
-{
-    for (size_t i = sizeof(void *); i != 0; i *= 2)
-    if (align == i)
-        return 0;
-    return EINVAL;
-}
-
-int posix_memalign(void **ptr, size_t align, size_t size)
-{
-    if (check_align(align))
-        return EINVAL;
-
-    int saved_errno = errno;
-    void *p = _aligned_malloc(size, align);
-    if (p == NULL)
-    {
-        errno = saved_errno;
-        return ENOMEM;
-    }
-
-    *ptr = p;
-    return 0;
-}
-
-#endif
-
-static void throwExceptionWithErrno(JNIEnv* env, const char* message) {
-    char err_msg[1024];
-    strerror_r(errno, err_msg, sizeof(err_msg));
-    unsigned long size = strlen(message) + strlen(err_msg) + 10;
-    char* str = malloc(size);
-    snprintf(str, size, "%s: %s", message, err_msg);
-
-    jstring javaMessage =  (*env)->NewStringUTF(env, str);
-    free(str);
-
-    jclass clazz =  (*env)->FindClass(env, 
"org/apache/bookkeeper/common/util/nativeio/NativeIOException");
-    jmethodID ctorMethod =  (*env)->GetMethodID(env, clazz, "<init>", 
"(Ljava/lang/String;I)V");
-    jobject myException =  (*env)->NewObject(env, clazz, ctorMethod, 
javaMessage, errno);
-    (*env)->Throw(env, myException);
-}
-
-static void throwException(JNIEnv* env, const char* message) {
-    (*env)->ThrowNew(env, (*env)->FindClass(env, 
"org/apache/bookkeeper/common/util/nativeio/NativeIOException"), message);
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    open
- * Signature: (Ljava/lang/String;II)I
- */
-JNIEXPORT jint JNICALL
-Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_open(
-    JNIEnv *env, jclass clazz, jstring path, jint javaFlags, jint mode) {
-    const char *cPath = (*env)->GetStringUTFChars(env, path, 0);
-
-    int flags = 0;
-    if (javaFlags & 0x01) {
-        flags |= O_CREAT;
-    }
-
-    if (javaFlags & 0x02) {
-        flags |= O_RDONLY;
-    }
-
-    if (javaFlags & 0x04) {
-        flags |= O_WRONLY;
-    }
-
-    if (javaFlags & 0x08) {
-        flags |= O_TRUNC;
-    }
-
-#ifdef __linux__
-    if (javaFlags & 0x10) {
-        flags |= O_DIRECT;
-    }
-#endif
-
-#ifndef _WIN32
-    if (javaFlags & 0x20) {
-        flags |= O_DSYNC;
-    }
-#endif
-
-    int fd = open(cPath, flags, mode);
-
-    (*env)->ReleaseStringUTFChars(env, path, cPath);
-
-    if (fd == -1) {
-      throwExceptionWithErrno(env, "Failed to open file");
-    }
-
-    return fd;
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    fsync
- * Signature: (I)I
- */
-JNIEXPORT jint JNICALL
-Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_fsync(JNIEnv * env,
-                                                               jclass clazz,
-                                                               jint fd) {
-    int res;
-
-    // Guarantee compatibility for winsows.
-    #ifdef _WIN32
-        res = _commit((int)fd);
-    #else
-        res = fsync((int)fd);
-    #endif
-
-    if (res == -1) {
-      throwExceptionWithErrno(env, "Failed to fsync");
-    }
-
-    return res;
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    fallocate
- * Signature: (IIJJ)I
- */
-JNIEXPORT jint JNICALL
-Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_fallocate(
-    JNIEnv* env, jclass clazz,
-    jint fd, jint mode, jlong offset, jlong len) {
-#ifdef __linux__
-    int res = fallocate(fd, mode, offset, len);
-    if (res == -1) {
-        throwExceptionWithErrno(env, "Failed to fallocate");
-    }
-    return res;
-#else
-    throwException(env, "fallocate is not available");
-    return -1;
-#endif
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    posix_fadvise
- * Signature: (IJJI)I
- */
-JNIEXPORT jint JNICALL
-Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_posix_1fadvise(
-    JNIEnv* env, jclass clazz,
-    jint fd, jlong offset, jlong len, jint flag) {
-#ifdef __linux__
-    int res = posix_fadvise(fd, offset, len, flag);
-    if (res == -1) {
-        throwExceptionWithErrno(env, "Failed to posix_fadvise");
-    }
-    return res;
-#else
-    throwException(env, "posix_fadvise is not available");
-    return -1;
-#endif
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    lseek
- * Signature: (IJI)J
- */
-JNIEXPORT jlong JNICALL
-Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_lseek(
-        JNIEnv* env, jclass clazz,
-        jint fd, jlong offset, jint whence) {
-    int res = lseek(fd, offset, whence);
-
-    if (res == -1) {
-        throwExceptionWithErrno(env, "Failed to lseek");
-    }
-
-    return res;
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    close
- * Signature: (I)I
- */
-JNIEXPORT jint JNICALL
-Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_close(JNIEnv* env, 
jclass clazz,
-                                                               jint fd) {
-    int res = close(fd);
-
-    if (res == -1) {
-         throwExceptionWithErrno(env, "Failed to close file");
-    }
-
-    return res;
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    pwrite
- * Signature: (IJIJ)I
- */
-JNIEXPORT jint JNICALL 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_pwrite
-    (JNIEnv* env, jclass clazz, jint fd, jlong pointer, jint count, jlong 
offset) {
-    int res = pwrite(fd, (const void*) pointer, count, offset);
-
-    if (res == -1) {
-      throwExceptionWithErrno(env, "Failed to write on file");
-    }
-
-    return res;
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    posix_memalign
- * Signature: (II)J
- */
-JNIEXPORT jlong JNICALL 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_posix_1memalign
-    (JNIEnv* env, jclass clazz, jint alignment, jint size) {
-    void* ptr;
-    int res = posix_memalign(&ptr, alignment, size);
-
-    if (res != 0) {
-      throwExceptionWithErrno(env, "Failed to allocate aligned memory");
-    }
-
-    return (jlong) ptr;
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    pread
- * Signature: (IJJJ)J
- */
-JNIEXPORT jlong JNICALL 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_pread
-    (JNIEnv * env, jclass clazz, jint fd, jlong pointer, jlong size, jlong 
offset) {
-
-    long res =  pread(fd, (void*) pointer, size, offset);
-
-    if (res == -1) {
-      throwExceptionWithErrno(env, "Failed to read from file");
-    }
-
-    return res;
-}
-
-/*
- * Class:     org_apache_bookkeeper_common_util_nativeio_NativeIOJni
- * Method:    free
- * Signature: (J)V
- */
-JNIEXPORT void JNICALL 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_free
-  (JNIEnv * env, jclass clazz, jlong pointer) {
-     free((const void*) pointer);
-}
-
diff --git a/native-io/src/main/native-io-jni/rust/Cargo.lock 
b/native-io/src/main/native-io-jni/rust/Cargo.lock
new file mode 100644
index 0000000000..83f6c34530
--- /dev/null
+++ b/native-io/src/main/native-io-jni/rust/Cargo.lock
@@ -0,0 +1,301 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "native-io"
+version = "4.18.0"
+dependencies = [
+ "jni",
+ "libc",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "walkdir"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/native-io/src/main/native-io-jni/rust/Cargo.toml 
b/native-io/src/main/native-io-jni/rust/Cargo.toml
new file mode 100644
index 0000000000..2e1322f2fe
--- /dev/null
+++ b/native-io/src/main/native-io-jni/rust/Cargo.toml
@@ -0,0 +1,41 @@
+#
+# 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]
+name = "native-io"
+version = "4.18.0"
+edition = "2021"
+license = "Apache-2.0"
+authors = ["Apache BookKeeper Community"]
+description = "Native I/O JNI library for Apache BookKeeper"
+
+[lib]
+name = "native_io"
+crate-type = ["cdylib"]
+
+[dependencies]
+jni = "0.21"
+libc = "0.2"
+
+[profile.release]
+opt-level = 3
+lto = true
+panic = "abort"
+strip = true
+codegen-units = 1
diff --git a/native-io/src/main/native-io-jni/rust/build.rs 
b/native-io/src/main/native-io-jni/rust/build.rs
new file mode 100644
index 0000000000..e61e66435f
--- /dev/null
+++ b/native-io/src/main/native-io-jni/rust/build.rs
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+//! Build script for native-io JNI library
+//! Sets the install_name for macOS dylib to match Java's expected library name
+
+fn main() {
+    let target = std::env::var("TARGET").unwrap_or_default();
+
+    // On macOS, set the install_name to libnative-io.jnilib
+    // This allows the library to be loaded with 
System.loadLibrary("native-io")
+    if target.contains("darwin") {
+        println!("cargo:rustc-cdylib-link-arg=-install_name");
+        println!("cargo:rustc-cdylib-link-arg=libnative-io.jnilib");
+    }
+
+    // Rebuild if the environment changes
+    println!("cargo:rerun-if-env-changed=TARGET");
+}
diff --git a/native-io/src/main/native-io-jni/rust/src/lib.rs 
b/native-io/src/main/native-io-jni/rust/src/lib.rs
new file mode 100644
index 0000000000..edca399b5a
--- /dev/null
+++ b/native-io/src/main/native-io-jni/rust/src/lib.rs
@@ -0,0 +1,346 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+//! Native I/O JNI library for Apache BookKeeper
+//!
+//! This library provides POSIX file I/O operations via JNI for 
high-performance direct I/O.
+//! It is a Rust implementation of the original C native_io_jni.c
+
+use jni::objects::{JClass, JString, JThrowable};
+use jni::sys::{jint, jlong};
+use jni::JNIEnv;
+use std::ffi::{c_void, CString};
+use std::ptr;
+
+// Java flags (from NativeIO.java)
+const O_CREAT: jint = 0x01;
+const O_RDONLY: jint = 0x02;
+const O_WRONLY: jint = 0x04;
+const O_TRUNC: jint = 0x08;
+const O_DIRECT: jint = 0x10;
+const O_DSYNC: jint = 0x20;
+
+// ============================================================================
+// Helper functions for exception throwing (equivalent to C functions)
+// ============================================================================
+
+/// Throws a NativeIOException with the current errno
+fn throw_exception_with_errno(env: &mut JNIEnv, message: &str) {
+    let errno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
+
+    throw_exception_with_error_code(env, message, errno);
+}
+
+fn throw_exception_with_error_code(env: &mut JNIEnv, message: &str, 
error_code: jint) {
+    let err_msg = std::io::Error::from_raw_os_error(error_code).to_string();
+    let full_msg = format!("{}: {}", message, err_msg);
+
+    if let Ok(class) =
+        
env.find_class("org/apache/bookkeeper/common/util/nativeio/NativeIOException")
+    {
+        if let Ok(msg) = env.new_string(&full_msg) {
+            let result = env.new_object(
+                class,
+                "(Ljava/lang/String;I)V",
+                &[(&msg).into(), error_code.into()],
+            );
+            if let Ok(exception) = result {
+                let _ = env.throw(JThrowable::from(exception));
+            }
+        }
+    }
+}
+
+/// Throws a NativeIOException without errno
+fn throw_exception(env: &mut JNIEnv, message: &str) {
+    if let Ok(class) =
+        
env.find_class("org/apache/bookkeeper/common/util/nativeio/NativeIOException")
+    {
+        let _ = env.throw_new(class, message);
+    }
+}
+
+// ============================================================================
+// JNI exported functions
+// ============================================================================
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: open
+/// Signature: (Ljava/lang/String;II)I
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_open(
+    mut env: JNIEnv,
+    _class: JClass,
+    path: JString,
+    java_flags: jint,
+    mode: jint,
+) -> jint {
+    // Convert Java flags to POSIX flags (same logic as C code)
+    let mut flags: i32 = 0;
+
+    if java_flags & O_CREAT != 0 {
+        flags |= libc::O_CREAT;
+    }
+    if java_flags & O_RDONLY != 0 {
+        flags |= libc::O_RDONLY;
+    }
+    if java_flags & O_WRONLY != 0 {
+        flags |= libc::O_WRONLY;
+    }
+    if java_flags & O_TRUNC != 0 {
+        flags |= libc::O_TRUNC;
+    }
+
+    // O_DIRECT is Linux only
+    #[cfg(target_os = "linux")]
+    if java_flags & O_DIRECT != 0 {
+        flags |= libc::O_DIRECT;
+    }
+
+    // O_DSYNC is not available on Windows
+    #[cfg(not(target_os = "windows"))]
+    if java_flags & O_DSYNC != 0 {
+        flags |= libc::O_DSYNC;
+    }
+
+    // Get the path string
+    let path_str: String = match env.get_string(&path) {
+        Ok(s) => s.into(),
+        Err(_) => {
+            throw_exception(&mut env, "Failed to get path string");
+            return -1;
+        }
+    };
+
+    let path_cstr = match CString::new(path_str) {
+        Ok(path) => path,
+        Err(_) => {
+            throw_exception(&mut env, "Path contains interior NUL byte");
+            return -1;
+        }
+    };
+
+    let fd = unsafe { libc::open(path_cstr.as_ptr(), flags, mode as 
libc::c_uint) };
+
+    if fd == -1 {
+        throw_exception_with_errno(&mut env, "Failed to open file");
+    }
+
+    fd
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: fsync
+/// Signature: (I)I
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_fsync(
+    mut env: JNIEnv,
+    _class: JClass,
+    fd: jint,
+) -> jint {
+    let res = unsafe { libc::fsync(fd) };
+
+    if res == -1 {
+        throw_exception_with_errno(&mut env, "Failed to fsync");
+    }
+
+    res as jint
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: fallocate
+/// Signature: (IIJJ)I
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_fallocate(
+    mut env: JNIEnv,
+    _class: JClass,
+    fd: jint,
+    mode: jint,
+    offset: jlong,
+    len: jlong,
+) -> jint {
+    // fallocate is Linux only
+    #[cfg(target_os = "linux")]
+    {
+        let res = unsafe { libc::fallocate(fd, mode, offset, len) };
+
+        if res == -1 {
+            throw_exception_with_errno(&mut env, "Failed to fallocate");
+        }
+
+        res
+    }
+
+    #[cfg(not(target_os = "linux"))]
+    {
+        throw_exception(&mut env, "fallocate is not available");
+        -1
+    }
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: posix_fadvise
+/// Signature: (IJJI)I
+/// Note: In JNI, underscores in method names are escaped as _1
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_posix_1fadvise(
+    mut env: JNIEnv,
+    _class: JClass,
+    fd: jint,
+    offset: jlong,
+    len: jlong,
+    flag: jint,
+) -> jint {
+    // posix_fadvise is Linux only
+    #[cfg(target_os = "linux")]
+    {
+        let res = unsafe { libc::posix_fadvise(fd, offset, len, flag) };
+
+        if res != 0 {
+            throw_exception_with_error_code(&mut env, "Failed to 
posix_fadvise", res);
+        }
+
+        res
+    }
+
+    #[cfg(not(target_os = "linux"))]
+    {
+        throw_exception(&mut env, "posix_fadvise is not available");
+        -1
+    }
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: lseek
+/// Signature: (IJI)J
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_lseek(
+    mut env: JNIEnv,
+    _class: JClass,
+    fd: jint,
+    offset: jlong,
+    whence: jint,
+) -> jlong {
+    let res = unsafe { libc::lseek(fd, offset, whence) };
+
+    if res == -1 {
+        throw_exception_with_errno(&mut env, "Failed to lseek");
+    }
+
+    res as jlong
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: close
+/// Signature: (I)I
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_close(
+    mut env: JNIEnv,
+    _class: JClass,
+    fd: jint,
+) -> jint {
+    let res = unsafe { libc::close(fd) };
+
+    if res == -1 {
+        throw_exception_with_errno(&mut env, "Failed to close file");
+    }
+
+    res as jint
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: pwrite
+/// Signature: (IJIJ)I
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_pwrite(
+    mut env: JNIEnv,
+    _class: JClass,
+    fd: jint,
+    pointer: jlong,
+    count: jint,
+    offset: jlong,
+) -> jint {
+    let res = unsafe { libc::pwrite(fd, pointer as *const c_void, count as 
usize, offset) };
+
+    if res == -1 {
+        throw_exception_with_errno(&mut env, "Failed to write on file");
+    }
+
+    res as jint
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: posix_memalign
+/// Signature: (II)J
+/// Note: In JNI, underscores in method names are escaped as _1
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_posix_1memalign(
+    mut env: JNIEnv,
+    _class: JClass,
+    alignment: jint,
+    size: jint,
+) -> jlong {
+    let mut ptr: *mut c_void = ptr::null_mut();
+
+    let res = unsafe { libc::posix_memalign(&mut ptr, alignment as usize, size 
as usize) };
+
+    if res != 0 {
+        throw_exception_with_error_code(&mut env, "Failed to allocate aligned 
memory", res);
+        return 0;
+    }
+
+    ptr as jlong
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: pread
+/// Signature: (IJJJ)J
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_pread(
+    mut env: JNIEnv,
+    _class: JClass,
+    fd: jint,
+    pointer: jlong,
+    size: jlong,
+    offset: jlong,
+) -> jlong {
+    let res = unsafe { libc::pread(fd, pointer as *mut c_void, size as usize, 
offset) };
+
+    if res == -1 {
+        throw_exception_with_errno(&mut env, "Failed to read from file");
+    }
+
+    res as jlong
+}
+
+/// Class: org.apache.bookkeeper.common.util.nativeio.NativeIOJni
+/// Method: free
+/// Signature: (J)V
+#[no_mangle]
+pub extern "system" fn 
Java_org_apache_bookkeeper_common_util_nativeio_NativeIOJni_free(
+    _env: JNIEnv,
+    _class: JClass,
+    pointer: jlong,
+) {
+    unsafe {
+        libc::free(pointer as *mut c_void);
+    }
+}
diff --git 
a/native-io/src/test/java/org/apache/bookkeeper/common/util/nativeio/NativeIOLibraryPathTest.java
 
b/native-io/src/test/java/org/apache/bookkeeper/common/util/nativeio/NativeIOLibraryPathTest.java
new file mode 100644
index 0000000000..4c7586ae30
--- /dev/null
+++ 
b/native-io/src/test/java/org/apache/bookkeeper/common/util/nativeio/NativeIOLibraryPathTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 org.apache.bookkeeper.common.util.nativeio;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class NativeIOLibraryPathTest {
+
+    private void assertLibraryCandidates(String osName, String osArch, 
String... expectedCandidates) {
+        List<String> expected = expectedCandidates.length == 0
+                ? Collections.emptyList()
+                : List.of(expectedCandidates);
+        assertEquals(expected, NativeIOLibraryPath.libraryCandidates(osName, 
osArch));
+    }
+
+    @Test
+    public void testLinuxAmd64() {
+        assertLibraryCandidates("Linux", "amd64", 
"/lib/linux-x86_64-gnu/libnative-io.so");
+    }
+
+    @Test
+    public void testLinuxX8664() {
+        assertLibraryCandidates("Linux", "x86_64", 
"/lib/linux-x86_64-gnu/libnative-io.so");
+    }
+
+    @Test
+    public void testLinuxAarch64() {
+        assertLibraryCandidates("Linux", "aarch64", 
"/lib/linux-aarch64-gnu/libnative-io.so");
+    }
+
+    @Test
+    public void testLinuxUnknownArch() {
+        assertThrows(IllegalArgumentException.class, () -> 
assertLibraryCandidates("Linux", "riscv64"));
+    }
+
+    @Test
+    public void testMacOsAmd64() {
+        assertLibraryCandidates("MacOS", "amd64", 
"/lib/macos-x86_64-gnu/libnative-io.so");
+    }
+
+    @Test
+    public void testMacAmd64() {
+        assertLibraryCandidates("Mac", "amd64", 
"/lib/macos-x86_64-gnu/libnative-io.so");
+    }
+
+    @Test
+    public void testMacOsWithSpaceAmd64() {
+        assertLibraryCandidates("Mac OS", "amd64", 
"/lib/macos-x86_64-gnu/libnative-io.so");
+    }
+
+    @Test
+    public void testWindowsAmd64() {
+        assertLibraryCandidates("Windows", "amd64", 
"/lib/windows-x86_64-gnu/libnative-io.so");
+    }
+
+    @Test
+    public void testExplicitPathProperty() {
+        assertEquals("/tmp/libnative-io.so",
+                
NativeIOLibraryPath.configuredLibraryPath("/tmp/libnative-io.so", null));
+    }
+
+    @Test
+    public void testExplicitPathEnv() {
+        assertEquals("/tmp/from-env.so",
+                NativeIOLibraryPath.configuredLibraryPath(null, 
"/tmp/from-env.so"));
+    }
+
+    @Test
+    public void testPropertyTakesPrecedenceOverEnv() {
+        assertEquals("/tmp/prop.so",
+                NativeIOLibraryPath.configuredLibraryPath("/tmp/prop.so", 
"/tmp/env.so"));
+    }
+
+    @Test
+    public void testBlankValuesReturnNull() {
+        assertNull(NativeIOLibraryPath.configuredLibraryPath("  ", ""));
+    }
+}
diff --git a/pom.xml b/pom.xml
index 8815ed0e76..a3ba5c7f3d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1234,6 +1234,9 @@
 
             <!-- test resources -->
             <exclude>**/test_conf_2.conf</exclude>
+
+            <!-- zig -->
+            <exclude>**/.zig-cache/**</exclude>
           </excludes>
           <consoleOutput>true</consoleOutput>
         </configuration>

Reply via email to