From 9d0fa620b491dcc989a894f679fe383d4474d0e8 Mon Sep 17 00:00:00 2001 From: hh Date: Tue, 26 Sep 2023 16:35:38 +0330 Subject: [PATCH 1/2] add jvm mock, jni improvments --- .gitignore | 1 + .../androidTest/java/land/fx/app/WNFSTest.kt | 28 +- lib/src/main/java/land/fx/wnfslib/Config.java | 2 - .../main/java/land/fx/wnfslib/Datastore.java | 4 + lib/src/main/java/land/fx/wnfslib/Fs.java | 397 +++++++----- .../land/fx/wnfslib/InMemoryDatastore.java | 92 +++ .../fx/wnfslib/exceptions/WnfsException.java | 2 +- wnfslib/Cargo.toml | 16 +- wnfslib/README.md | 8 +- wnfslib/jvm/Cargo.toml | 14 + wnfslib/jvm/src/example_proxy.rs | 52 ++ wnfslib/jvm/src/lib.rs | 96 +++ wnfslib/jvm/src/tests.rs | 41 ++ wnfslib/src/tests.rs | 576 ++++++++++++++++++ 14 files changed, 1162 insertions(+), 167 deletions(-) create mode 100644 lib/src/main/java/land/fx/wnfslib/InMemoryDatastore.java create mode 100644 wnfslib/jvm/Cargo.toml create mode 100644 wnfslib/jvm/src/example_proxy.rs create mode 100644 wnfslib/jvm/src/lib.rs create mode 100644 wnfslib/jvm/src/tests.rs create mode 100644 wnfslib/src/tests.rs diff --git a/.gitignore b/.gitignore index cf4572f..458ecd9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ target .vscode tmp wnfslib/Cargo.lock +wnfslib/datastore \ No newline at end of file diff --git a/appmock/src/androidTest/java/land/fx/app/WNFSTest.kt b/appmock/src/androidTest/java/land/fx/app/WNFSTest.kt index 50c7cf8..d0466c7 100755 --- a/appmock/src/androidTest/java/land/fx/app/WNFSTest.kt +++ b/appmock/src/androidTest/java/land/fx/app/WNFSTest.kt @@ -1,11 +1,13 @@ package land.fx.app import android.util.Log +import android.util.Base64 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.rules.ActivityScenarioRule import fulamobile.Fulamobile import land.fx.wnfslib.Fs.* +import land.fx.wnfslib.InMemoryDatastore import land.fx.wnfslib.Config import land.fx.wnfslib.result.* import land.fx.wnfslib.exceptions.* @@ -142,7 +144,11 @@ class WNFSTest { val message = byteArray.joinToString(", ") { it.toString() } Log.d(tag, msg + message) } + class ConvertFulaClient(private val fulaClient: fulamobile.Client): land.fx.wnfslib.Datastore{ + private var totalBytesPut = 0L // Keep track of the total bytes written + private var totalBytesGet = 0L // Keep track of the total bytes got + fun logByteArray(tag: String, msg: String, byteArray: ByteArray) { val message = byteArray.joinToString(", ") { it.toString() } Log.d(tag, msg + message) @@ -172,6 +178,22 @@ class WNFSTest { } @get:Rule val mainActivityRule = ActivityScenarioRule(MainActivity::class.java) + + private fun generateLargeTestFile(path: String): File { + val file = File(path, "largeTestFile2.txt") + file.outputStream().use { output -> + val buffer = ByteArray(1024) // 1KB buffer + val random = java.util.Random() + + // Write 1GB of random data to the file + repeat(58_576) { // 1GB = 1024 * 1024 KB + random.nextBytes(buffer) + output.write(buffer) + } + } + return file + } + @Test fun wnfs_overall() { val useInMemoryStore = false // Or determine this from some configurations or conditions @@ -200,7 +222,7 @@ class WNFSTest { Log.d("AppMock", "client created with id="+fulaClient.id()) - val keyPhrase = ("test").toByteArray(StandardCharsets.UTF_8) + val keyPhrase = ("test22").toByteArray(StandardCharsets.UTF_8) val digest: MessageDigest = MessageDigest.getInstance("SHA-256"); val wnfsKey: ByteArray = digest.digest(keyPhrase); @@ -227,12 +249,12 @@ class WNFSTest { val fileNames_initial: ByteArray = ls( client , config.cid - , "/" + UUID.randomUUID().toString() + , UUID.randomUUID().toString() ) Log.d("AppMock", "ls_initial. fileNames_initial="+String(fileNames_initial)) } catch (e: Exception) { val contains = e.message?.contains("find", true) - Log.d("AppMock", "ls_initial. error="+e.message) + Log.d("AppMock", "ls_initial_error. error="+e.message) assertEquals(contains, true) } diff --git a/lib/src/main/java/land/fx/wnfslib/Config.java b/lib/src/main/java/land/fx/wnfslib/Config.java index 60febb3..b470fab 100644 --- a/lib/src/main/java/land/fx/wnfslib/Config.java +++ b/lib/src/main/java/land/fx/wnfslib/Config.java @@ -1,7 +1,5 @@ package land.fx.wnfslib; -import androidx.annotation.NonNull; - public final class Config { private final String cid; diff --git a/lib/src/main/java/land/fx/wnfslib/Datastore.java b/lib/src/main/java/land/fx/wnfslib/Datastore.java index ab0b8ae..46e6e82 100644 --- a/lib/src/main/java/land/fx/wnfslib/Datastore.java +++ b/lib/src/main/java/land/fx/wnfslib/Datastore.java @@ -4,4 +4,8 @@ public interface Datastore { byte[] put(byte[] cid,byte[] data); byte[] get(byte[] cid); + + Long getTotalBytesGet(); + + Long getTotalBytesPut(); } diff --git a/lib/src/main/java/land/fx/wnfslib/Fs.java b/lib/src/main/java/land/fx/wnfslib/Fs.java index b787cd1..0105646 100644 --- a/lib/src/main/java/land/fx/wnfslib/Fs.java +++ b/lib/src/main/java/land/fx/wnfslib/Fs.java @@ -8,12 +8,16 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import org.json.JSONObject; import org.json.JSONArray; import land.fx.wnfslib.result.*; import land.fx.wnfslib.*; import land.fx.wnfslib.exceptions.WnfsException; + public final class Fs { private static native ConfigResult initNative(Datastore datastore, byte[] wnfsKey); @@ -42,229 +46,306 @@ public final class Fs { private static native BytesResult readFileNative(Datastore datastore, String cid, String path); + private static native String putNative(Datastore datastore, byte[] key, byte[] value); + + private static native String getNative(Datastore datastore, byte[] key); + + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); @NonNull public static Config init(Datastore datastore, byte[] wnfsKey) throws Exception { - try { - ConfigResult res = initNative(datastore, wnfsKey); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.init", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = initNative(datastore, wnfsKey); + if (res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.init", res.getReason()); + } + } catch (Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static void loadWithWNFSKey(Datastore datastore, byte[] wnfsKey, String cid) throws Exception { - try { - Result res = loadWithWNFSKeyNative(datastore, wnfsKey, cid); - if(res == null || !res.ok()) { - throw new WnfsException("Fs.loadWithWNFSKey", res.getReason()); + Future future = executor.submit(() -> { + try { + Result res = loadWithWNFSKeyNative(datastore, wnfsKey, cid); + if(res == null || !res.ok()) { + throw new WnfsException("Fs.loadWithWNFSKey for cid="+cid, res.getReason()); + } + return null; } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + catch(Exception e) { + throw new Exception(e.getMessage()); + } + }); + + future.get(); } @NonNull public static Config writeFileFromPath(Datastore datastore, String cid, String path, String filename) throws Exception { - try { - ConfigResult res = writeFileFromPathNative(datastore, cid, path, filename); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.writeFileFromPath", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = writeFileFromPathNative(datastore, cid, path, filename); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.writeFileFromPath for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static Config writeFileStreamFromPath(Datastore datastore, String cid, String path, String filename) throws Exception { - try { - ConfigResult res = writeFileStreamFromPathNative(datastore, cid, path, filename); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.writeFileStreamFromPath", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = writeFileStreamFromPathNative(datastore, cid, path, filename); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.writeFileStreamFromPath for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static Config writeFile(Datastore datastore, String cid, String path, byte[] content) throws Exception { - try { - ConfigResult res = writeFileNative(datastore, cid, path, content); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.writeFile", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = writeFileNative(datastore, cid, path, content); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.writeFile for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static byte[] ls(Datastore datastore, String cid, String path) throws Exception { - try { - - Log.d("wnfs", "JSONArray is reached2"); - BytesResult res = lsNative(datastore, cid, path); - Log.d("wnfs", "lsResult is reached: "); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.ls", res.getReason()); - } - /*JSONArray output = new JSONArray(); - byte[] rowSeparatorPattern = {33, 33, 33}; //!!! - byte[] itemSeparatorPattern = {63, 63, 63}; //??? - List rows = split(rowSeparatorPattern, lsResult); - for (byte[] element : rows) { - JSONObject obj = new JSONObject(); - List rowDetails = split(itemSeparatorPattern, element); - if (!rowDetails.isEmpty()) { - String name = new String(rowDetails.get(0), StandardCharsets.UTF_8); - if(!name.isEmpty()) { - obj.put("name", name); - if(rowDetails.size() >= 2) { - String creation = new String(rowDetails.get(1), StandardCharsets.UTF_8); - obj.put("creation", creation); - } else { - obj.put("creation", ""); - } - if(rowDetails.size() >= 3) { - String modification = new String(rowDetails.get(2), StandardCharsets.UTF_8); - obj.put("modification", modification); - } else { - obj.put("modification", ""); + Future future = executor.submit(() -> { + try { + + Log.d("wnfs", "JSONArray is reached2"); + BytesResult res = lsNative(datastore, cid, path); + Log.d("wnfs", "lsResult is reached: "); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.ls for cid="+cid, res.getReason()); + } + /*JSONArray output = new JSONArray(); + byte[] rowSeparatorPattern = {33, 33, 33}; //!!! + byte[] itemSeparatorPattern = {63, 63, 63}; //??? + List rows = split(rowSeparatorPattern, lsResult); + for (byte[] element : rows) { + JSONObject obj = new JSONObject(); + List rowDetails = split(itemSeparatorPattern, element); + if (!rowDetails.isEmpty()) { + String name = new String(rowDetails.get(0), StandardCharsets.UTF_8); + if(!name.isEmpty()) { + obj.put("name", name); + if(rowDetails.size() >= 2) { + String creation = new String(rowDetails.get(1), StandardCharsets.UTF_8); + obj.put("creation", creation); + } else { + obj.put("creation", ""); + } + if(rowDetails.size() >= 3) { + String modification = new String(rowDetails.get(2), StandardCharsets.UTF_8); + obj.put("modification", modification); + } else { + obj.put("modification", ""); + } + output.put(obj); } - output.put(obj); } - } - - }*/ - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + + }*/ + } + catch(Exception e) { + throw new Exception(e.getMessage()); + } + }); + + return future.get(); } @NonNull public static Config mkdir(Datastore datastore, String cid, String path) throws Exception { - try { - ConfigResult res = mkdirNative(datastore, cid, path); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.mkdir", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = mkdirNative(datastore, cid, path); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.mkdir for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static Config rm(Datastore datastore, String cid, String path) throws Exception { - try { - ConfigResult res = rmNative(datastore, cid, path); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.rm", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = rmNative(datastore, cid, path); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.rm for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static Config mv(Datastore datastore, String cid, String sourcePath, String targetPath) throws Exception { - try { - ConfigResult res = mvNative(datastore, cid, sourcePath, targetPath); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.mv", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = mvNative(datastore, cid, sourcePath, targetPath); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.mv for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static Config cp(Datastore datastore, String cid, String sourcePath, String targetPath) throws Exception { - try { - ConfigResult res = cpNative(datastore, cid, sourcePath, targetPath); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.cp", res.getReason()); + Future future = executor.submit(() -> { + try { + ConfigResult res = cpNative(datastore, cid, sourcePath, targetPath); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.cp for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + }); + + return future.get(); } @NonNull public static String readFileToPath(Datastore datastore, String cid, String path, String filename) throws Exception { - try{ - StringResult res = readFileToPathNative(datastore, cid, path, filename); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.readFileToPathNative", res.getReason()); + Future future = executor.submit(() -> { + try{ + StringResult res = readFileToPathNative(datastore, cid, path, filename); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.readFileToPathNative for cid="+cid, res.getReason()); + } } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + catch(Exception e) { + throw new Exception(e.getMessage()); + } + }); + + return future.get(); } @NonNull public static String readFilestreamToPath(Datastore datastore, String cid, String path, String filename) throws Exception { - try{ - StringResult res = readFilestreamToPathNative(datastore, cid, path, filename); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.readFilestreamToPathNative", res.getReason()); + Future future = executor.submit(() -> { + try{ + StringResult res = readFilestreamToPathNative(datastore, cid, path, filename); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.readFilestreamToPathNative for cid="+cid, res.getReason()); + } } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } + catch(Exception e) { + throw new Exception(e.getMessage()); + } + }); + + return future.get(); } public static byte[] readFile(Datastore datastore, String cid, String path) throws Exception { - try{ - BytesResult res = readFileNative(datastore, cid, path); - if(res != null && res.ok()) { - return res.getResult(); - } else { - throw new WnfsException("Fs.readFileNative", res.getReason()); + Future future = executor.submit(() -> { + try{ + BytesResult res = readFileNative(datastore, cid, path); + if(res != null && res.ok()) { + return res.getResult(); + } else { + throw new WnfsException("Fs.readFileNative for cid="+cid, res.getReason()); + } + } + catch(Exception e) { + throw new Exception(e.getMessage()); + } + }); + + return future.get(); + } + + @NonNull + public static void put(Datastore datastore, byte[] key, byte[] value) throws Exception { + try { + putNative(datastore, key, value); + } + catch(Exception e) { + throw new Exception(e.getMessage()); + } + } + + @NonNull + public static void get(Datastore datastore, byte[] key) throws Exception { + try { + getNative(datastore, key); + } + catch(Exception e) { + throw new Exception(e.getMessage()); } - } - catch(Exception e) { - throw new Exception(e.getMessage()); - } } public static native void initRustLogger(); diff --git a/lib/src/main/java/land/fx/wnfslib/InMemoryDatastore.java b/lib/src/main/java/land/fx/wnfslib/InMemoryDatastore.java new file mode 100644 index 0000000..0086138 --- /dev/null +++ b/lib/src/main/java/land/fx/wnfslib/InMemoryDatastore.java @@ -0,0 +1,92 @@ +package land.fx.wnfslib; + +import land.fx.wnfslib.Config; +import land.fx.wnfslib.Datastore; +import land.fx.wnfslib.result.*; +import land.fx.wnfslib.exceptions.*; +import java.io.File; +import java.util.Base64; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.logging.Logger; + + +public final class InMemoryDatastore implements Datastore { + private static Logger LOGGER = Logger.getLogger("InfoLogging"); + private final ConcurrentHashMap store = new ConcurrentHashMap(); + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + private Long totalBytesPut = 0L; // Keep track of the total bytes written + private Long totalBytesGet = 0L; // Keep track of the total bytes got + + public InMemoryDatastore() { + + } + + public static String hex(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte aByte : bytes) { + result.append(String.format("%02x", aByte)); + // upper case + // result.append(String.format("%02X", aByte)); + } + return result.toString(); + } + + private void logByteArray(String tag, String msg, byte[] byteArray) { + LOGGER.info(msg + hex(byteArray)); + } + + public byte[] put(byte[] cid, byte[] data) { + Future future = executor.submit(() -> { + String key = Base64.getEncoder().encodeToString(cid); + logByteArray("InMemoryDatastore", "put data=", data); + logByteArray("InMemoryDatastore", "put cid=", cid); + store.put(key, data); + totalBytesPut += data.length; // Increment total bytes written + LOGGER.info("data put successfully for cid: " + key); + return cid; + }); + + try { + return future.get(); + } catch (Exception e) { + // TODO: handle exception + return null; + } + } + + public byte[] get(byte[] cid) { + Future future = executor.submit(() -> { + String key = Base64.getEncoder().encodeToString(cid); + logByteArray("InMemoryDatastore", "get cid=", cid); + if (!store.containsKey(key)){ + throw new Exception("Data not found for CID: " + key); + } + byte[] data = store.get(key); + totalBytesGet += data.length; // Increment total bytes written + logByteArray("InMemoryDatastore", "get returned data=", data); + return data; + }); + + try { + return future.get(); + } catch (Exception e) { + // TODO: handle exception + return null; + } + } + // Add a method to retrieve the total bytes written + public Long getTotalBytesPut() { + return totalBytesPut; + } + // Add a method to retrieve the total bytes written + public Long getTotalBytesGet() { + return totalBytesGet; + } +} \ No newline at end of file diff --git a/lib/src/main/java/land/fx/wnfslib/exceptions/WnfsException.java b/lib/src/main/java/land/fx/wnfslib/exceptions/WnfsException.java index 8301ac9..4da5e0b 100644 --- a/lib/src/main/java/land/fx/wnfslib/exceptions/WnfsException.java +++ b/lib/src/main/java/land/fx/wnfslib/exceptions/WnfsException.java @@ -11,6 +11,6 @@ public WnfsException() {} // Constructor that accepts a message public WnfsException(String func ,String reason) { - super(String.format("An Error Occured in Fs.%s: %s", func, reason)); + super(String.format("An Error Occured in WNFS.%s: %s", func, reason)); } } \ No newline at end of file diff --git a/wnfslib/Cargo.toml b/wnfslib/Cargo.toml index 6ed1871..f8a8fb9 100644 --- a/wnfslib/Cargo.toml +++ b/wnfslib/Cargo.toml @@ -3,6 +3,9 @@ name = "wnfslib-android" version = "1.8.1" edition = "2021" +# [workspace] +# members = ["jvm"] + [lib] name = "wnfslib" crate-type = ["cdylib", "staticlib"] @@ -22,9 +25,18 @@ image = "0.24.2" url = { version = "2.2.2", features = ["serde"] } rand = "0.8.5" kv = "0.24.0" +sha256 = "1.1.1" +once_cell = "1.17.0" +libc = "0.2.139" -jni = { version = "0.19.0", default-features = false } +jni = { version = "0.19.0" } android_logger = "0.11.0" # comment this while debbuging using vscode+rust-plugin. -ndk = "0.6.0" +# ndk = "0.6.0" + +[dev-dependencies] +jvm = {path = "jvm"} + +[dev-dependencies.jni] +features = ["invocation"] \ No newline at end of file diff --git a/wnfslib/README.md b/wnfslib/README.md index f6eed2a..8abb9eb 100644 --- a/wnfslib/README.md +++ b/wnfslib/README.md @@ -5,5 +5,11 @@ This is the home of the _Rust_ WNFS library for Android which exposes multi-plat ## Compile ```bash -TODO +export JAVA_HOME=path/to/java8 +cd PROJECT/lib/src/main/java; +$JAVA_HOME/jre/bin/javac -cp ./ ./land/fx/wnfslib/InMemoryDatastore.java +$JAVA_HOME/jre/bin/javac -cp ./ ./land/fx/wnfslib/result/* +$JAVA_HOME/jre/bin/javac -cp ./ ./land/fx/wnfslib/exceptions/* +cd PROJECT/wnfslib; +RUST_BACKTRACE=1 LD_LIBRARY_PATH=$JAVA_HOME/lib/server cargo test ``` diff --git a/wnfslib/jvm/Cargo.toml b/wnfslib/jvm/Cargo.toml new file mode 100644 index 0000000..218fe47 --- /dev/null +++ b/wnfslib/jvm/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "jvm" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +# [lib] +# name = "jvm" +# path = "src/lib.rs" +[dependencies] +jni = { version = "0.19.0", features = ["invocation"] } +[build-dependencies.walkdir] +version = "2" \ No newline at end of file diff --git a/wnfslib/jvm/src/example_proxy.rs b/wnfslib/jvm/src/example_proxy.rs new file mode 100644 index 0000000..f134b62 --- /dev/null +++ b/wnfslib/jvm/src/example_proxy.rs @@ -0,0 +1,52 @@ +#![allow(dead_code)] + +use jni::{ + errors::*, + objects::{GlobalRef, JValue}, + sys::jint, + Executor, JNIEnv, +}; + +/// A test example of a native-to-JNI proxy +#[derive(Clone)] +pub struct AtomicIntegerProxy { + exec: Executor, + obj: GlobalRef, +} + +impl AtomicIntegerProxy { + /// Creates a new instance of `AtomicIntegerProxy` + pub fn new(exec: Executor, init_value: jint) -> Result { + let obj = exec.with_attached(|env: &JNIEnv| { + env.new_global_ref(env.new_object( + "java/util/concurrent/atomic/AtomicInteger", + "(I)V", + &[JValue::from(init_value)], + )?) + })?; + Ok(AtomicIntegerProxy { exec, obj }) + } + + /// Gets a current value from java object + pub fn get(&mut self) -> Result { + self.exec + .with_attached(|env| env.call_method(&self.obj, "get", "()I", &[])?.i()) + } + + /// Increments a value of java object and then gets it + pub fn increment_and_get(&mut self) -> Result { + self.exec.with_attached(|env| { + env.call_method(&self.obj, "incrementAndGet", "()I", &[])? + .i() + }) + } + + /// Adds some value to the value of java object and then gets a resulting value + pub fn add_and_get(&mut self, delta: jint) -> Result { + let delta = JValue::from(delta); + self.exec.with_attached(|env| { + env.call_method(&self.obj, "addAndGet", "(I)I", &[delta])? + .i() + }) + } +} \ No newline at end of file diff --git a/wnfslib/jvm/src/lib.rs b/wnfslib/jvm/src/lib.rs new file mode 100644 index 0000000..f8490f3 --- /dev/null +++ b/wnfslib/jvm/src/lib.rs @@ -0,0 +1,96 @@ +pub mod example_proxy; +pub mod jvm { +use std::sync::{Arc, Once}; + +use jni::{ + errors::Result, objects::JValue, sys::jint, AttachGuard, InitArgsBuilder, JNIEnv, JNIVersion, + JavaVM, +}; + +pub fn jvm() -> &'static Arc { + static mut JVM: Option> = None; + static INIT: Once = Once::new(); + + INIT.call_once(|| { + let jvm_args = InitArgsBuilder::new() + .version(JNIVersion::V8) + .option("-Xcheck:jni") + .option("-Djava.class.path=/home/ox26a/Projects/functionland/wnfs-android/lib/src/main/java/") + .build() + .unwrap_or_else(|e| panic!("{:#?}", e)); + + let jvm = JavaVM::new(jvm_args).unwrap_or_else(|e| panic!("{:#?}", e)); + unsafe { + JVM = Some(Arc::new(jvm)); + } + }); + + unsafe { JVM.as_ref().unwrap() } +} + +#[allow(dead_code)] +pub fn call_java_abs(env: &JNIEnv, value: i32) -> i32 { + env.call_static_method( + "java/lang/Math", + "abs", + "(I)I", + &[JValue::from(value as jint)], + ) + .unwrap() + .i() + .unwrap() +} + +#[allow(dead_code)] +pub fn attach_current_thread() -> AttachGuard<'static> { + jvm() + .attach_current_thread() + .expect("failed to attach jvm thread") +} + +#[allow(dead_code)] +pub fn attach_current_thread_as_daemon() -> JNIEnv<'static> { + jvm() + .attach_current_thread_as_daemon() + .expect("failed to attach jvm daemon thread") +} + +#[allow(dead_code)] +pub fn attach_current_thread_permanently() -> JNIEnv<'static> { + jvm() + .attach_current_thread_permanently() + .expect("failed to attach jvm thread permanently") +} + +#[allow(dead_code)] +pub fn detach_current_thread() { + jvm().detach_current_thread() +} + +pub fn print_exception(env: &JNIEnv) { + let exception_occurred = env.exception_check().unwrap_or_else(|e| panic!("{:?}", e)); + if exception_occurred { + env.exception_describe() + .unwrap_or_else(|e| panic!("{:?}", e)); + } +} + +#[allow(dead_code)] +pub fn unwrap(env: &JNIEnv, res: Result) -> T { + res.unwrap_or_else(|e| { + print_exception(&env); + panic!("{:#?}", e); + }) +} + +pub struct Jvm { + pub env: JNIEnv<'static> +} + +impl Jvm { + pub fn new() -> Self { + Self { env: attach_current_thread_as_daemon()} + } +} +} +pub mod tests; \ No newline at end of file diff --git a/wnfslib/jvm/src/tests.rs b/wnfslib/jvm/src/tests.rs new file mode 100644 index 0000000..f49aec8 --- /dev/null +++ b/wnfslib/jvm/src/tests.rs @@ -0,0 +1,41 @@ + +#[cfg(test)] +#[test] +pub fn jmap_push_and_iterate() { + use jni::objects::{JMap, JObject}; + + use crate::jvm::{Jvm, unwrap}; + + let jvm = Jvm::new(); + let env = jvm.env; + let data = &["hello", "world", "from", "test"]; + // Create a new map. Use LinkedHashMap to have predictable iteration order + let map_object = unwrap(&env, env.new_object("java/util/LinkedHashMap", "()V", &[])); + let map = unwrap(&env, JMap::from_env(&env, map_object)); + + // Push all strings + unwrap( + &env, + data.iter().try_for_each(|s| { + env.new_string(s) + .map(JObject::from) + .and_then(|s| map.put(s, s).map(|_| ())) + }), + ); + + // Collect the keys using the JMap iterator + let mut collected = Vec::new(); + unwrap( + &env, + map.iter().and_then(|mut iter| { + iter.try_for_each(|e| { + env.get_string(e.0.into()) + .map(|s| collected.push(String::from(s))) + }) + }), + ); + + let orig = data.to_vec(); + assert_eq!(orig, collected); + +} diff --git a/wnfslib/src/tests.rs b/wnfslib/src/tests.rs new file mode 100644 index 0000000..aaacbd6 --- /dev/null +++ b/wnfslib/src/tests.rs @@ -0,0 +1,576 @@ +#[cfg(test)] +mod android_tests { + extern crate jni; + use crate::android::*; + use jni::{ + objects::{JClass, JObject, JString}, + signature::JavaType, + sys::{jbyteArray, jobject, jsize, jstring}, + JNIEnv, + }; + use jvm::jvm::{unwrap, Jvm}; + use std::fs::File; + use std::io::Write; + use std::{fs, ptr}; + use wnfs::common::CODEC_DAG_CBOR; + use wnfsutils::{ + blockstore::{FFIFriendlyBlockStore, FFIStore}, + kvstore::KVBlockStore, + }; + + fn create_dummy_file(filename: &str, file_size: usize) -> std::io::Result<()> { + let chunk = [0u8; 1024]; // 1 KB of zeros + let mut file = File::create(filename)?; + + for _ in 0..(file_size / 1024) { + file.write_all(&chunk)?; + } + + Ok(()) + } + + fn generate_dummy_data(size: usize) -> Vec { + vec![0u8; size] + } + + fn get_string(env: JNIEnv, obj: jobject) -> String { + let jni_string = JString::from(obj); + println!("jni_string"); + let r_string: String = env + .get_string(jni_string.into()) + .expect("Failed to parse cid") + .into(); + return r_string; + } + + fn java_byte_array_to_string( + env: JNIEnv, + java_byte_array: jobject, + ) -> Result { + let java_byte_array = unsafe { JObject::from(java_byte_array) }; + let byte_array = java_byte_array.into_inner() as jbyteArray; + let len = env.get_array_length(byte_array)? as jsize; + let mut buf: Vec = vec![0; len as usize]; + + env.get_byte_array_region(byte_array, 0, &mut buf)?; + + let u8_buf: Vec = unsafe { std::mem::transmute(buf) }; + let result_str = String::from_utf8(u8_buf).ok().unwrap(); + + Ok(result_str) + } + + fn get_cid(env: JNIEnv, obj: jobject) -> JString { + let out = env + .call_method(obj, "getCid", "()Ljava/lang/String;", &[]) + .unwrap_or_else(|_err: jni::errors::Error| panic!("wnfsError test ERE1: {}", _err)) + .l() + .unwrap_or_else(|_err: jni::errors::Error| panic!("wnfsError test HERE2: {}", _err)); + return JString::from(out); + } + + fn get_result(env: JNIEnv, obj: JObject, retClass: String) -> jobject { + let obj_class = env + .get_object_class(obj) + .expect("Couldn't get object class"); + + let out = env + .call_method(obj, "getResult", "()Ljava/lang/Object;", &[]) + .unwrap_or_else(|_err: jni::errors::Error| panic!("wnfsError test ERE1: {}", _err)) + .l() + .unwrap_or_else(|_err: jni::errors::Error| panic!("wnfsError test HERE2: {}", _err)); + let config_result_class = env.find_class(retClass).expect("Class not found"); + if env + .is_instance_of(obj, config_result_class) + .expect("is_instance_of failed") + { + // obj is of type ConfigResult + // You can now safely call methods that are specific to ConfigResult on obj + } + return out.into_inner(); + } + + fn convert_to_jstring<'a>( + env: &'a JNIEnv, + rust_string: String, + ) -> jni::errors::Result> { + env.new_string(rust_string) + } + + #[test] + fn test_overall() { + unsafe { + let itteration = 15; + let jvm = Jvm::new(); + + let env = jvm.env; + let class = env + .find_class("land/fx/wnfslib/result/ConfigResult") + .expect("Error on class"); + + let empty_key: Vec = vec![0; 32]; + let jni_wnfs_key = vec_to_jbyte_array(env, empty_key); + let jni_fula_client = unwrap( + &env, + env.new_object("land/fx/wnfslib/InMemoryDatastore", "()V", &[]), + ); + let jclass: JClass<'_> = JObject::null().into(); + let config = + Java_land_fx_wnfslib_Fs_initNative(env, jclass, jni_fula_client, jni_wnfs_key); + + let jni_config = get_result(env, config.into(), "land/fx/wnfslib/Config".into()); + let jni_cid = get_cid(env, jni_config.into()); + let cid_value = deserialize_cid(env, jni_cid.into()); + println!("cid_value: {:?}", cid_value); + assert!( + !cid_value.to_string().is_empty(), + "Cid value should not be empty" + ); + + //mkdir + println!("*******************Starting mkdir******************"); + let rust_path = "root".to_string(); + let java_path = + convert_to_jstring(&env, rust_path).expect("Couldn't convert to JString"); + let config = Java_land_fx_wnfslib_Fs_mkdirNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + java_path, + ); + let jni_config = get_result(env, config.into(), "land/fx/wnfslib/Config".into()); + let mut jni_cid = get_cid(env, jni_config.into()); + let cid_value = deserialize_cid(env, jni_cid.into()); + println!("cid_value mkdir: {:?}", cid_value); + assert!( + !cid_value.to_string().is_empty(), + "Cid value should not be empty" + ); + + //mkdir test in itteration + let mkdir_itteration = 2; + println!( + "cid_value before {} mkdir: {:?}", + mkdir_itteration, cid_value + ); + for i in 1..=2 { + // Loop 10 times + println!("*******************Starting mkdir {}******************", i); + + // Create a unique directory name by appending the loop counter to the base name + let rust_path = format!("root/test_{}", i); + + let java_path = + convert_to_jstring(&env, rust_path).expect("Couldn't convert to JString"); + + let config = Java_land_fx_wnfslib_Fs_mkdirNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + java_path, + ); + + let jni_config = get_result(env, config.into(), "land/fx/wnfslib/Config".into()); + jni_cid = get_cid(env, jni_config.into()); + let cid_value = deserialize_cid(env, jni_cid.into()); + + println!("cid_value mkdir {}: {:?}", i, cid_value); + + assert!( + !cid_value.to_string().is_empty(), + "Cid value should not be empty for mkdir {}", + i + ); + let _ = env.delete_local_ref(jni_config.into()); + let _ = env.delete_local_ref(config.into()); + } + let cid_value = deserialize_cid(env, jni_cid.into()); + println!( + "cid_value after all {} mkdir: {:?} from jni_cid", + mkdir_itteration, cid_value + ); + + //ls1 + println!("*******************Starting ls1******************"); + let filenames_initial = Java_land_fx_wnfslib_Fs_lsNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + serialize_string(env, "root".into()), + ); + let jni_filenames_initial = get_result(env, filenames_initial.into(), "[B".into()); + let filenames = java_byte_array_to_string(env, jni_filenames_initial) + .ok() + .unwrap(); + println!("filenames mkdir2: {:?}", filenames); + assert!((1..=mkdir_itteration).all(|i| filenames.contains(&format!("test_{}", i)))); + + //Small writes test in itteration + let write_itteration = 15; + println!( + "cid_value before {} write: {:?}", + write_itteration, cid_value + ); + for i in 1..=write_itteration { + println!("**************cid_value before test{}: {:?}", i, cid_value); + println!( + "*******************Starting small write{}******************", + i + ); + let mut file = File::create(format!("test{}.txt", i)).ok().unwrap(); + // Write content into the file + let content = format!("Hello World {}", i); + let _ = file.write_all(content.as_bytes()); + + let config = Java_land_fx_wnfslib_Fs_writeFileStreamFromPathNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + serialize_string(env, format!("root/test.{}.txt", i).into()), + serialize_string(env, format!("test{}.txt", i).into()), + ); + + let jni_config = get_result(env, config.into(), "land/fx/wnfslib/Config".into()); + jni_cid = get_cid(env, jni_config.into()); + let cid_value = deserialize_cid(env, jni_cid.into()); + + println!("**************cid_value after test{}: {:?}", i, cid_value); + assert!( + !cid_value.to_string().is_empty(), + "Cid value should not be empty" + ); + let _ = env.delete_local_ref(jni_config.into()); + let _ = env.delete_local_ref(config.into()); + } + let cid_value = deserialize_cid(env, jni_cid.into()); + println!( + "cid_value after all {} write: {:?} from jni_cid", + write_itteration, cid_value + ); + + //ls1 + println!("*******************Starting ls2******************"); + let filenames_initial = Java_land_fx_wnfslib_Fs_lsNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + serialize_string(env, "root".into()), + ); + let jni_filenames_initial = get_result(env, filenames_initial.into(), "[B".into()); + let filenames = java_byte_array_to_string(env, jni_filenames_initial) + .ok() + .unwrap(); + println!("filenames wrtiefile2: {:?}", filenames); + assert!((1..=write_itteration).all(|i| filenames.contains(&format!("test.{}.txt", i)))); + + //write_file_stream large + println!("*******************Starting write_file_stream******************"); + let file_size = 50 * 1024 * 1024; // 60 MB + let _ = create_dummy_file("largefile_test.bin", file_size); + let config = Java_land_fx_wnfslib_Fs_writeFileStreamFromPathNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + serialize_string(env, "root/largeFileStream.bin".into()), + serialize_string(env, "largefile_test.bin".into()), + ); + let jni_config = get_result(env, config.into(), "land/fx/wnfslib/Config".into()); + let jni_cid = get_cid(env, jni_config.into()); + let cid_value = deserialize_cid(env, jni_cid.into()); + println!( + "**************cid_value writeFileStreamLarge: {:?}", + cid_value + ); + assert!( + !cid_value.to_string().is_empty(), + "Cid value should not be empty" + ); + + //read_file_Stream + println!("*******************Starting read_file_Stream******************"); + let _config = Java_land_fx_wnfslib_Fs_readFilestreamToPathNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + serialize_string(env, "root/largeFileStream.bin".into()), + serialize_string(env, "largefile_test_read.bin".into()), + ); + let original_filesize = fs::metadata("largefile_test.bin").ok().unwrap().len(); + let read_filesize = fs::metadata("largefile_test_read.bin").ok().unwrap().len(); + println!( + "original filsezie: {:?} vs read filesize: {:?}", + original_filesize, read_filesize + ); + assert_eq!(original_filesize, read_filesize, "File sizes do not match!"); + + //ls2 + println!("*******************Starting ls2******************"); + let filenames_initial = Java_land_fx_wnfslib_Fs_lsNative( + env, + jclass, + jni_fula_client, + jni_cid.into(), + serialize_string(env, "root".into()), + ); + let jni_filenames_initial = get_result(env, filenames_initial.into(), "[B".into()); + let filenames = java_byte_array_to_string(env, jni_filenames_initial) + .ok() + .unwrap(); + println!("filenames writeFileStreamLarge: {:?}", filenames); + assert!(filenames.contains("largeFileStream.bin")); + + //clean up + let _ = fs::remove_file("largefile_test_read.bin"); + let _ = fs::remove_file("largefile_test.bin"); + let _ = fs::remove_file("test1.txt"); + let _ = fs::remove_file("test2.txt"); + let _ = fs::remove_file("test3.txt"); + + // let names: Vec = filenames_initial.result.into(); + // println!("ls_initial. filenames_initial={:?}", names); + // // Write file + // let test_content = "Hello, World!"; + // fs::write("./tmp/test.txt", test_content.to_owned()).expect("Unable to write file"); + + // // Read large file + // { + // let large_data = vec![0u8; 500 * 1024 * 1024]; + // cid = write_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/test_large.bin".to_string()), + // large_data.to_owned().into(), + // ); + + // let content_large = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/test_large.bin".to_string()), + // ); + + // let content: Vec = content_large.result.into(); + // assert_eq!(content.to_owned(), large_data.to_owned()); + // if true { + // return + // } + // } + // // Read file + // { + // cid = write_file_from_path_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompath.txt".to_string()), + // RustString::from("./tmp/test.txt".to_string()), + // ); + + // let content_from_path = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompath.txt".to_string()), + // ); + + // let content = String::from_utf8(content_from_path.result.into()).unwrap(); + // assert_eq!(content, test_content.to_owned().to_string()); + // println!("read_file_from_path. content={}", content); + // } + // // Read content from path to path + // { + // let content_from_path_topath = read_file_to_path_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompath.txt".to_string()), + // RustString::from("./tmp/test2.txt".to_string()), + // ); + // let content_str: String = (content_from_path_topath).result.into(); + // println!("content_from_path_topath={}", content_str); + // let read_content = fs::read_to_string(content_str).expect("Unable to read file"); + // assert_eq!(read_content, test_content.to_string()); + // println!("read_file_from_path_of_read_to. content={}", read_content); + // } + // // Read content from file stream to path + // { + // let content_stream_from_path_topath = read_filestream_to_path_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompath.txt".to_string()), + // RustString::from("./tmp/teststream.txt".to_string()), + // ); + // let content_str: String = content_stream_from_path_topath.result.into(); + // println!("content_stream_from_path_topath={}", content_str); + // let read_content = fs::read_to_string(content_str).expect("Unable to read file"); + // assert_eq!(read_content, test_content.to_string()); + // println!("read_file_from_path_of_read_to. content={}", read_content); + // } + // // CP: target folder must exists + // { + // let _len: usize = 0; + // let _capacity: usize = 0; + + // cid = mkdir_native( + // blockstore, + // cid.into(), + // RustString::from("root/test1".to_string()), + // ); + + // cid = cp_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompath.txt".to_string()), + // RustString::from("root/testfrompathcp.txt".to_string()), + // ); + + // let content_cp = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompathcp.txt".to_string()), + // ); + // let content: String = String::from_utf8(content_cp.result.into()).unwrap(); + // println!("cp. content_cp={}", content); + // assert_eq!(content, test_content.to_string()); + // } + // // MV: target folder must exists + // { + // let len: usize = 0; + // let capacity: usize = 0; + // cid = mv_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompath.txt".to_string()), + // RustString::from("root/testfrompathmv.txt".to_string()), + // ); + + // let content_mv = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompathmv.txt".to_string()), + // ); + // println!("len: {}, cap: {}", len, capacity); + // let content: String = String::from_utf8(content_mv.result.into()).unwrap(); + // println!("mv. content_mv={}", content); + // assert_eq!(content, test_content.to_string()); + // } + // // RM#1 + // { + // let len: usize = 0; + // let capacity: usize = 0; + // cid = rm_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompathmv.txt".to_string()), + // ); + + // let content_rm1 = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompathmv.txt".to_string()), + // ); + // println!("len: {}, cap: {}", len, capacity); + // let content: String = String::from_utf8(content_rm1.result.into()).unwrap(); + // println!("rm#1. content_rm#1={}", content); + // assert_eq!(content, "".to_string()); + // } + // // RM#2 + // { + // let len: usize = 0; + // let capacity: usize = 0; + // cid = rm_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompathcp.txt".to_string()), + // ); + + // let content_rm2 = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/testfrompathcp.txt".to_string()), + // ); + // println!("len: {}, cap: {}", len, capacity); + // let content: String = String::from_utf8(content_rm2.result.into()).unwrap(); + // println!("rm#1. content_rm#1={}", content); + // assert_eq!(content, "".to_string()); + // } + // // + // { + // println!( + // "********************** test content: {}", + // test_content.to_owned() + // ); + // cid = write_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/test.txt".to_string()), + // test_content.as_bytes().to_vec().into(), + // ); + + // cid = mkdir_native( + // blockstore, + // cid.into(), + // RustString::from("root/test1".to_string()), + // ); + + // let content_ls = ls_native( + // blockstore, + // cid.into(), + // RustString::from("root".to_string()), + // ); + + // let file_names = String::from_utf8(content_ls.result.into()).unwrap(); + // println!("ls. fileNames={}", file_names); + // let content_test = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/test.txt".to_string()), + // ); + + // let content: String = String::from_utf8(content_test.result.into()).unwrap(); + // println!("read. content={}", content); + // assert_eq!(content, test_content.to_string()); + // } + // println!("All tests before reload passed"); + + // // Testing reload Directory + // { + // println!( + // "wnfs12 Testing reload with cid={} & wnfsKey={:?}", + // cid.to_string(), + // wnfs_key_string + // ); + // load_with_wnfs_key_native( + // blockstore, + // wnfs_key_string.to_owned().into(), + // cid.into(), + // ); + + // let content_reloaded = read_file_native( + // blockstore, + // cid.into(), + // RustString::from("root/test.txt".to_string()), + // ); + // let content: String = String::from_utf8(content_reloaded.result.into()).unwrap(); + // println!("read. content={}", content); + // assert_eq!(content, test_content.to_string()); + // } + // // Read content from path to path (reloaded) + // { + // let content_from_path_topath_reloaded = read_file_to_path_native( + // blockstore, + // cid.into(), + // RustString::from("root/test.txt".to_string()), + // RustString::from("./tmp/test2.txt".to_string()), + // ); + // let content_str: String = content_from_path_topath_reloaded.result.into(); + // println!("content_from_path_topath_reloaded={}", content_str); + // let read_content = fs::read_to_string(content_str).expect("Unable to read file"); + // assert_eq!(read_content, test_content.to_string()); + // println!("read_file_from_path_of_read_to. content={}", read_content); + // } + } + } +} From a91584a1216a046140f9575cb2e8d91f5e9877b1 Mon Sep 17 00:00:00 2001 From: hh Date: Tue, 26 Sep 2023 16:38:47 +0330 Subject: [PATCH 2/2] small fix --- wnfslib/Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/wnfslib/Cargo.toml b/wnfslib/Cargo.toml index f8a8fb9..594384d 100644 --- a/wnfslib/Cargo.toml +++ b/wnfslib/Cargo.toml @@ -3,9 +3,6 @@ name = "wnfslib-android" version = "1.8.1" edition = "2021" -# [workspace] -# members = ["jvm"] - [lib] name = "wnfslib" crate-type = ["cdylib", "staticlib"] @@ -33,7 +30,7 @@ libc = "0.2.139" jni = { version = "0.19.0" } android_logger = "0.11.0" # comment this while debbuging using vscode+rust-plugin. -# ndk = "0.6.0" +ndk = "0.6.0" [dev-dependencies] jvm = {path = "jvm"}